@valtimo/process-management 13.42.0 → 13.44.0

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.
Files changed (41) hide show
  1. package/fesm2022/valtimo-process-management.mjs +1688 -278
  2. package/fesm2022/valtimo-process-management.mjs.map +1 -1
  3. package/lib/components/process-management-builder/panel/expression-autocomplete.d.ts +37 -0
  4. package/lib/components/process-management-builder/panel/expression-autocomplete.d.ts.map +1 -0
  5. package/lib/components/process-management-builder/panel/index.d.ts +2 -0
  6. package/lib/components/process-management-builder/panel/index.d.ts.map +1 -1
  7. package/lib/components/process-management-builder/panel/read-only-properties-panel.d.ts +17 -0
  8. package/lib/components/process-management-builder/panel/read-only-properties-panel.d.ts.map +1 -0
  9. package/lib/components/process-management-builder/panel/valtimo-properties-provider.d.ts +3 -1
  10. package/lib/components/process-management-builder/panel/valtimo-properties-provider.d.ts.map +1 -1
  11. package/lib/components/process-management-builder/process-management-builder.component.d.ts +45 -3
  12. package/lib/components/process-management-builder/process-management-builder.component.d.ts.map +1 -1
  13. package/lib/components/process-management-list/process-management-list.component.d.ts +1 -0
  14. package/lib/components/process-management-list/process-management-list.component.d.ts.map +1 -1
  15. package/lib/components/process-management-upload/process-management-upload.component.d.ts +46 -1
  16. package/lib/components/process-management-upload/process-management-upload.component.d.ts.map +1 -1
  17. package/lib/constants/process-management.test-ids.d.ts +26 -0
  18. package/lib/constants/process-management.test-ids.d.ts.map +1 -1
  19. package/lib/models/index.d.ts +2 -0
  20. package/lib/models/index.d.ts.map +1 -1
  21. package/lib/models/process-bean.model.d.ts +19 -0
  22. package/lib/models/process-bean.model.d.ts.map +1 -0
  23. package/lib/models/process-definition-import.model.d.ts +37 -0
  24. package/lib/models/process-definition-import.model.d.ts.map +1 -0
  25. package/lib/models/process-management.model.d.ts +11 -11
  26. package/lib/models/process-management.model.d.ts.map +1 -1
  27. package/lib/models/process.model.d.ts +2 -0
  28. package/lib/models/process.model.d.ts.map +1 -1
  29. package/lib/models/window.model.d.ts +2 -0
  30. package/lib/models/window.model.d.ts.map +1 -1
  31. package/lib/services/index.d.ts +1 -0
  32. package/lib/services/index.d.ts.map +1 -1
  33. package/lib/services/process-bean.service.d.ts +15 -0
  34. package/lib/services/process-bean.service.d.ts.map +1 -0
  35. package/lib/services/process-management-editor.service.d.ts +15 -0
  36. package/lib/services/process-management-editor.service.d.ts.map +1 -1
  37. package/lib/services/process-management.service.d.ts +5 -2
  38. package/lib/services/process-management.service.d.ts.map +1 -1
  39. package/lib/utils/process-management-builder.utils.d.ts +8 -0
  40. package/lib/utils/process-management-builder.utils.d.ts.map +1 -1
  41. package/package.json +1 -1
@@ -7,16 +7,15 @@ import { ReactiveFormsModule, Validators, FormsModule } from '@angular/forms';
7
7
  import * as i9 from '@ngx-translate/core';
8
8
  import { TranslateModule, TranslateService } from '@ngx-translate/core';
9
9
  import * as i1$1 from '@valtimo/components';
10
- import { FitPageDirective, RenderInPageHeaderDirective, OverflowMenuComponent, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, ConfirmationModalModule, ViewType, CarbonListModule, CARBON_CONSTANTS, RenderInBodyComponent, ValtimoCdsModalDirective, WidgetModule } from '@valtimo/components';
10
+ import { FitPageDirective, RenderInPageHeaderDirective, ConfirmationModalModule, OverflowMenuModule, ViewType, CarbonListModule, CARBON_CONSTANTS, RenderInBodyComponent, ValtimoCdsModalDirective, WidgetModule } from '@valtimo/components';
11
11
  import * as i5 from '@valtimo/process-link';
12
12
  import { ProcessLinkModule, ProcessLinkStateService, ProcessLinkStepService, ProcessLinkButtonService } from '@valtimo/process-link';
13
13
  import * as i2$2 from 'carbon-components-angular';
14
14
  import { LoadingModule, DropdownModule, SelectModule, ButtonModule, IconModule, TagModule, ToggleModule, TooltipModule, FileUploaderModule, ModalModule, LayerModule, NotificationModule, RadioModule } from 'carbon-components-angular';
15
15
  import * as i1 from '@angular/common/http';
16
- import { HttpErrorResponse } from '@angular/common/http';
17
- import { Deploy16, ListChecked16, Return16, Upload16 } from '@carbon/icons';
18
- import * as i2$1 from '@valtimo/shared';
19
- import { BaseApiService, getCaseManagementRouteParams, getBuildingBlockManagementRouteParams, ROLE_ADMIN } from '@valtimo/shared';
16
+ import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
17
+ import * as i2 from '@valtimo/shared';
18
+ import { BaseApiService, InterceptorSkipHeader, InterceptorSkip, getCaseManagementRouteParams, getBuildingBlockManagementRouteParams, ROLE_ADMIN } from '@valtimo/shared';
20
19
  import { useService, BpmnPropertiesPanelModule, BpmnPropertiesProviderModule, CamundaPlatformPropertiesProviderModule } from 'bpmn-js-properties-panel';
21
20
  import Modeler from 'bpmn-js/lib/Modeler';
22
21
  import camundaPlatformBehaviors from 'camunda-bpmn-js-behaviors/lib/camunda-platform';
@@ -24,15 +23,17 @@ import CamundaBpmnModdle from 'camunda-bpmn-moddle/resources/camunda.json';
24
23
  import { isEqual } from 'lodash';
25
24
  import { filter, BehaviorSubject, Subject, Subscription, combineLatest, switchMap, of, tap, map as map$1, startWith, from, take, merge } from 'rxjs';
26
25
  import { distinctUntilChanged, map, filter as filter$1 } from 'rxjs/operators';
27
- import * as i2 from '@valtimo/form';
26
+ import * as i2$1 from '@valtimo/form';
28
27
  import { toObservable } from '@angular/core/rxjs-interop';
29
28
  import { is } from 'bpmn-js/lib/util/ModelUtil';
30
29
  import { html } from 'htm/preact';
30
+ import { View16, ViewOff16, Upload16 } from '@carbon/icons';
31
31
  import * as i3 from 'ngx-logger';
32
32
  import * as i7 from '@valtimo/process';
33
33
  import * as i8 from '@angular/router';
34
34
  import { RouterModule } from '@angular/router';
35
35
  import * as i10 from '@valtimo/plugin';
36
+ import { PluginConfigurationMappingComponent } from '@valtimo/plugin';
36
37
  import { AuthGuardService } from '@valtimo/security';
37
38
 
38
39
  /*
@@ -85,11 +86,37 @@ const PROCESS_MANAGEMENT_LIST_TEST_IDS = {
85
86
  uploadButton: 'processManagementUploadButton',
86
87
  createProcessButton: 'processManagementCreateProcessButton',
87
88
  };
89
+ const PROCESS_MANAGEMENT_UPLOAD_TEST_IDS = {
90
+ fileUploader: 'processManagementUploadFileUploader',
91
+ cancelButton: 'processManagementUploadCancelButton',
92
+ submitButton: 'processManagementUploadSubmitButton',
93
+ backButton: 'processManagementUploadBackButton',
94
+ importButton: 'processManagementUploadImportButton',
95
+ closeButton: 'processManagementUploadCloseButton',
96
+ };
97
+ /**
98
+ * The "Process link" group the Valtimo properties provider adds to the bpmn-js
99
+ * properties panel. The surrounding panel is rendered by bpmn-js itself and
100
+ * cannot carry test ids — only this group is ours.
101
+ */
102
+ const PROCESS_LINK_PANEL_TEST_IDS = {
103
+ createButton: 'processLinkPanelCreateButton',
104
+ editButton: 'processLinkPanelEditButton',
105
+ unlinkButton: 'processLinkPanelUnlinkButton',
106
+ };
88
107
  const PROCESS_MANAGEMENT_BUILDER_TEST_IDS = {
89
108
  draftToggle: 'processManagementBuilderDraftToggle',
90
109
  startsCaseToggle: 'processManagementBuilderStartsCaseToggle',
91
110
  startableByUserToggle: 'processManagementBuilderStartableByUserToggle',
92
111
  deployButton: 'processManagementBuilderDeployButton',
112
+ exportOption: 'processManagementBuilderExportOption',
113
+ markersVisibilityToggle: 'processManagementBuilderMarkersVisibilityToggle',
114
+ versionDropdown: 'processManagementBuilderVersionDropdown',
115
+ validateButton: 'processManagementBuilderValidateButton',
116
+ moreButton: 'processManagementBuilderMoreButton',
117
+ readOnlyTag: 'processManagementBuilderReadOnlyTag',
118
+ systemProcessTag: 'processManagementBuilderSystemProcessTag',
119
+ validationErrors: 'processManagementBuilderValidationErrors',
93
120
  };
94
121
 
95
122
  /*
@@ -108,8 +135,30 @@ const PROCESS_MANAGEMENT_BUILDER_TEST_IDS = {
108
135
  * limitations under the License.
109
136
  */
110
137
 
138
+ class ProcessBeanService extends BaseApiService {
139
+ constructor(httpClient, configService) {
140
+ super(httpClient, configService);
141
+ this.httpClient = httpClient;
142
+ this.configService = configService;
143
+ }
144
+ getProcessBeans() {
145
+ return this.httpClient.get(this.getApiUrl('/management/v1/process-bean'));
146
+ }
147
+ getProcessBean(beanName) {
148
+ return this.httpClient.get(this.getApiUrl(`/management/v1/process-bean/${beanName}`));
149
+ }
150
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessBeanService, deps: [{ token: i1.HttpClient }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
151
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessBeanService, providedIn: 'root' }); }
152
+ }
153
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessBeanService, decorators: [{
154
+ type: Injectable,
155
+ args: [{
156
+ providedIn: 'root',
157
+ }]
158
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ConfigService }] });
159
+
111
160
  /*
112
- * Copyright 2015-2025 Ritense BV, the Netherlands.
161
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
113
162
  *
114
163
  * Licensed under EUPL, Version 1.2 (the "License");
115
164
  * you may not use this file except in compliance with the License.
@@ -154,6 +203,39 @@ class ProcessManagementEditorService {
154
203
  setValidationErrors(errors) {
155
204
  this._validationErrors$.next(errors);
156
205
  }
206
+ get dismissedAutofills() {
207
+ return this._dismissedAutofills$.getValue();
208
+ }
209
+ get autofillDismissed$() {
210
+ return this._autofillDismissed$.asObservable();
211
+ }
212
+ dismissAutofill(activityId) {
213
+ const current = this._dismissedAutofills$.getValue();
214
+ current.add(activityId);
215
+ this._dismissedAutofills$.next(new Set(current));
216
+ this._autofillDismissed$.next(activityId);
217
+ }
218
+ clearDismissedAutofills() {
219
+ this._dismissedAutofills$.next(new Set());
220
+ }
221
+ isAutofillDismissed(activityId) {
222
+ return this._dismissedAutofills$.getValue().has(activityId);
223
+ }
224
+ get autofilledElements() {
225
+ return this._autofilledElements$.getValue();
226
+ }
227
+ setAutofilledElements(elements) {
228
+ this._autofilledElements$.next(elements);
229
+ }
230
+ getAutofillForActivity(activityId) {
231
+ return this._autofilledElements$.getValue().find(e => e.activityId === activityId);
232
+ }
233
+ get editingAllowed() {
234
+ return this._editingAllowed$.getValue();
235
+ }
236
+ setEditingAllowed(editingAllowed) {
237
+ this._editingAllowed$.next(editingAllowed);
238
+ }
157
239
  constructor(processLinkService, formService) {
158
240
  this.processLinkService = processLinkService;
159
241
  this.formService = formService;
@@ -165,6 +247,10 @@ class ProcessManagementEditorService {
165
247
  this._formDefinitionOptions$ = new BehaviorSubject([]);
166
248
  this._validationErrors$ = new BehaviorSubject([]);
167
249
  this.validationErrors$ = this._validationErrors$.asObservable();
250
+ this._dismissedAutofills$ = new BehaviorSubject(new Set());
251
+ this._autofillDismissed$ = new Subject();
252
+ this._autofilledElements$ = new BehaviorSubject([]);
253
+ this._editingAllowed$ = new BehaviorSubject(true);
168
254
  this._updatingBpmnView = false;
169
255
  this._activityIdBusinessIdMap = {};
170
256
  this._subscriptions = new Subscription();
@@ -275,12 +361,12 @@ class ProcessManagementEditorService {
275
361
  }
276
362
  }));
277
363
  }
278
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementEditorService, deps: [{ token: i5.ProcessLinkService }, { token: i2.FormService }], target: i0.ɵɵFactoryTarget.Injectable }); }
364
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementEditorService, deps: [{ token: i5.ProcessLinkService }, { token: i2$1.FormService }], target: i0.ɵɵFactoryTarget.Injectable }); }
279
365
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementEditorService }); }
280
366
  }
281
367
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementEditorService, decorators: [{
282
368
  type: Injectable
283
- }], ctorParameters: () => [{ type: i5.ProcessLinkService }, { type: i2.FormService }] });
369
+ }], ctorParameters: () => [{ type: i5.ProcessLinkService }, { type: i2$1.FormService }] });
284
370
 
285
371
  /*
286
372
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -326,7 +412,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
326
412
  }] });
327
413
 
328
414
  /*
329
- * Copyright 2015-2025 Ritense BV, the Netherlands.
415
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
330
416
  *
331
417
  * Licensed under EUPL, Version 1.2 (the "License");
332
418
  * you may not use this file except in compliance with the License.
@@ -358,7 +444,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
358
444
  */
359
445
 
360
446
  /*
361
- * Copyright 2015-2025 Ritense BV, the Netherlands.
447
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
448
+ *
449
+ * Licensed under EUPL, Version 1.2 (the "License");
450
+ * you may not use this file except in compliance with the License.
451
+ * You may obtain a copy of the License at
452
+ *
453
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
454
+ *
455
+ * Unless required by applicable law or agreed to in writing, software
456
+ * distributed under the License is distributed on an "AS IS" basis,
457
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
458
+ * See the License for the specific language governing permissions and
459
+ * limitations under the License.
460
+ */
461
+
462
+ /*
463
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
464
+ *
465
+ * Licensed under EUPL, Version 1.2 (the "License");
466
+ * you may not use this file except in compliance with the License.
467
+ * You may obtain a copy of the License at
468
+ *
469
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
470
+ *
471
+ * Unless required by applicable law or agreed to in writing, software
472
+ * distributed under the License is distributed on an "AS IS" basis,
473
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
474
+ * See the License for the specific language governing permissions and
475
+ * limitations under the License.
476
+ */
477
+
478
+ /*
479
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
362
480
  *
363
481
  * Licensed under EUPL, Version 1.2 (the "License");
364
482
  * you may not use this file except in compliance with the License.
@@ -378,7 +496,7 @@ const PROCESS_MANAGEMENT_ENDPOINTS = {
378
496
  };
379
497
 
380
498
  /*
381
- * Copyright 2015-2025 Ritense BV, the Netherlands.
499
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
382
500
  *
383
501
  * Licensed under EUPL, Version 1.2 (the "License");
384
502
  * you may not use this file except in compliance with the License.
@@ -409,6 +527,21 @@ const PROCESS_MANAGEMENT_ENDPOINTS = {
409
527
  * limitations under the License.
410
528
  */
411
529
 
530
+ /*
531
+ * Copyright 2015-2025 Ritense BV, the Netherlands.
532
+ *
533
+ * Licensed under EUPL, Version 1.2 (the "License");
534
+ * you may not use this file except in compliance with the License.
535
+ * You may obtain a copy of the License at
536
+ *
537
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
538
+ *
539
+ * Unless required by applicable law or agreed to in writing, software
540
+ * distributed under the License is distributed on an "AS IS" basis,
541
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
542
+ * See the License for the specific language governing permissions and
543
+ * limitations under the License.
544
+ */
412
545
  class ProcessManagementService extends BaseApiService {
413
546
  get caseDefinitionKey() {
414
547
  return this._definitionKey$.getValue() ?? '';
@@ -475,6 +608,18 @@ class ProcessManagementService extends BaseApiService {
475
608
  updateProcessDefinitionCaseDefinitionProperties(caseDefinitionKey, caseDefinitionVersionTag, processDefinitionId, body) {
476
609
  return this.httpClient.put(this.getApiUrl(`/management/v1/case-definition/${caseDefinitionKey}/version/${caseDefinitionVersionTag}/process/${processDefinitionId}/properties`), body);
477
610
  }
611
+ exportProcessDefinition(processDefinitionId) {
612
+ return this.httpClient.get(this.getApiUrl(`/management/v1/process-definition/${processDefinitionId}/export`), { observe: 'response', responseType: 'blob', headers: InterceptorSkipHeader });
613
+ }
614
+ previewProcessDefinitionImport(file) {
615
+ return this.httpClient.post(this.getApiUrl('/management/v1/process-definition/import/preview'), file, { headers: new HttpHeaders().set(InterceptorSkip, '400') });
616
+ }
617
+ importProcessDefinition(file, pluginConfigurationMappings) {
618
+ if (pluginConfigurationMappings) {
619
+ file.set('pluginConfigurationMappings', new Blob([JSON.stringify(pluginConfigurationMappings)], { type: 'application/json' }));
620
+ }
621
+ return this.httpClient.post(this.getApiUrl('/management/v1/process-definition/import'), file, { headers: new HttpHeaders().set(InterceptorSkip, '400') });
622
+ }
478
623
  validateProcessDefinition(request) {
479
624
  return this.httpClient.post(this.getApiUrl('/management/v1/process-definition/validate'), request);
480
625
  }
@@ -499,7 +644,7 @@ class ProcessManagementService extends BaseApiService {
499
644
  }
500
645
  return object;
501
646
  }
502
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementService, deps: [{ token: i1.HttpClient }, { token: i2$1.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
647
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementService, deps: [{ token: i1.HttpClient }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
503
648
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementService, providedIn: 'root' }); }
504
649
  }
505
650
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementService, decorators: [{
@@ -507,7 +652,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
507
652
  args: [{
508
653
  providedIn: 'root',
509
654
  }]
510
- }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2$1.ConfigService }] });
655
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.ConfigService }] });
511
656
 
512
657
  /*
513
658
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -629,6 +774,10 @@ const DisableBpmnWriteModule = {
629
774
  ],
630
775
  move: ['value', null],
631
776
  resizeHandles: ['value', { addResizer: () => { }, removeResizers: () => { } }],
777
+ bendpoints: ['value', {}],
778
+ bendpointMove: ['value', { start: () => { } }],
779
+ bendpointMovePreview: ['value', {}],
780
+ connectionSegmentMove: ['value', { start: () => { } }],
632
781
  };
633
782
  const disableCommands = (editor) => {
634
783
  const commandStack = editor.get('commandStack');
@@ -759,6 +908,9 @@ class ValtimoPropertiesProvider {
759
908
  get pluginTranslationService() {
760
909
  return window.pluginTranslationService;
761
910
  }
911
+ get processLinkService() {
912
+ return window.processLinkService;
913
+ }
762
914
  constructor(propertiesPanel) {
763
915
  propertiesPanel.registerProvider(500, this);
764
916
  }
@@ -774,6 +926,8 @@ class ValtimoPropertiesProvider {
774
926
  getGroups(element) {
775
927
  const processLink = this.processManagementEditorService.processLinksForSelectedDefinition.find(processLink => processLink.activityId === element.id) || null;
776
928
  const elementErrors = this.processManagementEditorService.validationErrors.filter(error => error.elementId === element.id);
929
+ const autofillInfo = this.processManagementEditorService.getAutofillForActivity(element.id);
930
+ const isAutofillDismissed = this.processManagementEditorService.isAutofillDismissed(element.id);
777
931
  return (groups) => {
778
932
  const generalGroup = groups.find((g) => g.id === 'general');
779
933
  if (generalGroup) {
@@ -795,6 +949,23 @@ class ValtimoPropertiesProvider {
795
949
  };
796
950
  groups.unshift(errorGroup);
797
951
  }
952
+ if (autofillInfo && !isAutofillDismissed) {
953
+ const targetGroupId = this.getGroupIdForModificationType(autofillInfo.modificationType);
954
+ const targetGroup = groups.find((g) => g.id === targetGroupId);
955
+ if (targetGroup) {
956
+ const notificationEntry = {
957
+ id: 'autofilledNotificationEntry',
958
+ activityId: element.id,
959
+ element,
960
+ translateService: this.translateService,
961
+ processManagementEditorService: this.processManagementEditorService,
962
+ processLinkService: this.processLinkService,
963
+ component: AutofilledNotificationElement,
964
+ };
965
+ targetGroup.entries.unshift(notificationEntry);
966
+ targetGroup.shouldOpen = true;
967
+ }
968
+ }
798
969
  if (is(element, 'bpmn:UserTask') ||
799
970
  is(element, 'bpmn:StartEvent') ||
800
971
  is(element, 'bpmn:ServiceTask') ||
@@ -803,18 +974,31 @@ class ValtimoPropertiesProvider {
803
974
  is(element, 'bpmn:IntermediateThrowEvent') ||
804
975
  is(element, 'bpmn:IntermediateCatchEvent') ||
805
976
  is(element, 'bpmn:CallActivity')) {
806
- const customGroup = {
807
- id: 'customRootGroup',
808
- label: 'Process link',
809
- entries: [this.createCustomRootElement(element, processLink)],
810
- groupType: 'root',
811
- };
812
- this.addAsSecondOrFirst(groups, customGroup);
977
+ const editingAllowed = this.processManagementEditorService.editingAllowed;
978
+ if (editingAllowed || processLink) {
979
+ const customGroup = {
980
+ id: 'customRootGroup',
981
+ label: 'Process link',
982
+ entries: [this.createCustomRootElement(element, processLink, editingAllowed)],
983
+ groupType: 'root',
984
+ };
985
+ this.addAsSecondOrFirst(groups, customGroup);
986
+ }
813
987
  }
814
988
  return groups;
815
989
  };
816
990
  }
817
- createCustomRootElement(element, processLink) {
991
+ getGroupIdForModificationType(modificationType) {
992
+ const groupMapping = {
993
+ SERVICE_TASK_EXPRESSION: 'CamundaPlatform__Implementation',
994
+ SEND_TASK_EXPRESSION: 'CamundaPlatform__Implementation',
995
+ MESSAGE_EVENT_EXPRESSION: 'message',
996
+ TIMER_DURATION: 'timer',
997
+ CALL_ACTIVITY_BUSINESS_KEY: 'CamundaPlatform__BusinessKey',
998
+ };
999
+ return groupMapping[modificationType] || 'general';
1000
+ }
1001
+ createCustomRootElement(element, processLink, editingAllowed) {
818
1002
  return {
819
1003
  translateService: this.translateService,
820
1004
  processManagementEditorService: this.processManagementEditorService,
@@ -822,18 +1006,20 @@ class ValtimoPropertiesProvider {
822
1006
  id: 'customRootElement',
823
1007
  processLink,
824
1008
  element,
1009
+ editingAllowed,
825
1010
  component: CustomRootElement,
826
- isEdited: () => false,
1011
+ isEdited: (node) => node && !!node.value,
827
1012
  };
828
1013
  }
829
1014
  }
830
1015
  const CustomRootElement = (props) => {
831
- const { element, processLink, translateService, processManagementEditorService, pluginTranslationService, } = props;
1016
+ const { element, processLink, translateService, processManagementEditorService, pluginTranslationService, editingAllowed, } = props;
832
1017
  const modeling = useService('modeling');
833
1018
  const elementRegistry = useService('elementRegistry');
834
1019
  const editProcessLinkText = translateService.instant('interface.edit');
835
1020
  const unlinkText = translateService.instant('processLink.unlink');
836
1021
  const createText = translateService.instant('processLink.create');
1022
+ const viewText = translateService.instant('interface.view');
837
1023
  const getModalParams = () => {
838
1024
  const currentElement = elementRegistry.get(element.id) || element;
839
1025
  return {
@@ -874,55 +1060,26 @@ const CustomRootElement = (props) => {
874
1060
  };
875
1061
  const processLinkFormDefinitionId = processLink?.formDefinitionId;
876
1062
  const processLinkFormDefinitionName = processManagementEditorService.formDefinitionOptions.find(option => option.id === processLinkFormDefinitionId)?.name;
877
- if (processLinkFormDefinitionName) {
878
- return html `<div class="process-link-properties-panel">
879
- <div class="process-link-properties-panel__header">
880
- <span class="process-link-properties-panel__title">${processLinkFormDefinitionName}</span>
881
-
882
- <cds-tag
883
- class="cds--tag cds--tag--blue cds--tag--md cds--layout--size-md cds-tag--no-margin"
884
- ><span class="cds--tag__label">
885
- ${translateService.instant('processLinkType.form')}
886
- </span>
887
- </cds-tag>
888
- </div>
889
-
890
- <div class="process-link-properties-panel__buttons">
891
- <button
892
- class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
893
- onClick=${handleUnlinkClick}
894
- >
895
- ${unlinkText}
896
- </button>
897
-
1063
+ const hiddenInput = html `<input
1064
+ type="hidden"
1065
+ class="bio-properties-panel-input"
1066
+ value=${processLink ? 'configured' : ''}
1067
+ />`;
1068
+ const wrapEntry = (content) => html `<div data-entry-id=${props.id}>${hiddenInput}${content}</div>`;
1069
+ const linkedButtons = !editingAllowed
1070
+ ? html `<div class="process-link-properties-panel__buttons">
898
1071
  <button
899
1072
  class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1073
+ data-test-id=${PROCESS_LINK_PANEL_TEST_IDS.editButton}
900
1074
  onClick=${handleEditClick}
901
1075
  >
902
- ${editProcessLinkText}
1076
+ ${viewText}
903
1077
  </button>
904
- </div>
905
- </div>`;
906
- }
907
- const processLinkFormFlowDefinitionKey = processLink?.formFlowDefinitionKey;
908
- if (processLinkFormFlowDefinitionKey) {
909
- return html `<div class="process-link-properties-panel">
910
- <div class="process-link-properties-panel__header">
911
- <span class="process-link-properties-panel__title"
912
- >${processLinkFormFlowDefinitionKey}</span
913
- >
914
-
915
- <cds-tag
916
- class="cds--tag cds--tag--teal cds--tag--md cds--layout--size-md cds-tag--no-margin"
917
- ><span class="cds--tag__label">
918
- ${translateService.instant('processLinkType.form-flow')}
919
- </span>
920
- </cds-tag>
921
- </div>
922
-
923
- <div class="process-link-properties-panel__buttons">
1078
+ </div>`
1079
+ : html `<div class="process-link-properties-panel__buttons">
924
1080
  <button
925
1081
  class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
1082
+ data-test-id=${PROCESS_LINK_PANEL_TEST_IDS.unlinkButton}
926
1083
  onClick=${handleUnlinkClick}
927
1084
  >
928
1085
  ${unlinkText}
@@ -930,165 +1087,848 @@ const CustomRootElement = (props) => {
930
1087
 
931
1088
  <button
932
1089
  class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1090
+ data-test-id=${PROCESS_LINK_PANEL_TEST_IDS.editButton}
933
1091
  onClick=${handleEditClick}
934
1092
  >
935
1093
  ${editProcessLinkText}
936
1094
  </button>
937
- </div>
938
- </div>`;
1095
+ </div>`;
1096
+ if (processLinkFormDefinitionName) {
1097
+ return wrapEntry(html `<div class="process-link-properties-panel">
1098
+ <div class="process-link-properties-panel__header">
1099
+ <span class="process-link-properties-panel__title">${processLinkFormDefinitionName}</span>
1100
+
1101
+ <cds-tag
1102
+ class="cds--tag cds--tag--blue cds--tag--md cds--layout--size-md cds-tag--no-margin"
1103
+ ><span class="cds--tag__label">
1104
+ ${translateService.instant('processLinkType.form')}
1105
+ </span>
1106
+ </cds-tag>
1107
+ </div>
1108
+
1109
+ ${linkedButtons}
1110
+ </div>`);
1111
+ }
1112
+ const processLinkFormFlowDefinitionKey = processLink?.formFlowDefinitionKey;
1113
+ if (processLinkFormFlowDefinitionKey) {
1114
+ return wrapEntry(html `<div class="process-link-properties-panel">
1115
+ <div class="process-link-properties-panel__header">
1116
+ <span class="process-link-properties-panel__title"
1117
+ >${processLinkFormFlowDefinitionKey}</span
1118
+ >
1119
+
1120
+ <cds-tag
1121
+ class="cds--tag cds--tag--teal cds--tag--md cds--layout--size-md cds-tag--no-margin"
1122
+ ><span class="cds--tag__label">
1123
+ ${translateService.instant('processLinkType.form-flow')}
1124
+ </span>
1125
+ </cds-tag>
1126
+ </div>
1127
+
1128
+ ${linkedButtons}
1129
+ </div>`);
939
1130
  }
940
1131
  const buildingBlockDefinitionKey = processLink?.buildingBlockDefinitionKey;
941
1132
  const buildingBlockDefinitionVersion = processLink?.buildingBlockDefinitionVersionTag;
942
1133
  if (buildingBlockDefinitionKey) {
943
- return html `<div class="process-link-properties-panel">
944
- <div class="process-link-properties-panel__header">
945
- <span class="process-link-properties-panel__title"
946
- >${buildingBlockDefinitionKey} (${buildingBlockDefinitionVersion})</span
947
- >
1134
+ return wrapEntry(html `<div class="process-link-properties-panel">
1135
+ <div class="process-link-properties-panel__header">
1136
+ <span class="process-link-properties-panel__title"
1137
+ >${buildingBlockDefinitionKey} (${buildingBlockDefinitionVersion})</span
1138
+ >
948
1139
 
949
- <cds-tag
950
- class="cds--tag cds--tag--green cds--tag--md cds--layout--size-md cds-tag--no-margin"
951
- ><span class="cds--tag__label">
952
- ${translateService.instant('processLinkType.building-block')}
953
- </span>
954
- </cds-tag>
955
- </div>
1140
+ <cds-tag
1141
+ class="cds--tag cds--tag--green cds--tag--md cds--layout--size-md cds-tag--no-margin"
1142
+ ><span class="cds--tag__label">
1143
+ ${translateService.instant('processLinkType.building-block')}
1144
+ </span>
1145
+ </cds-tag>
1146
+ </div>
956
1147
 
957
- <div class="process-link-properties-panel__buttons">
958
- <button
959
- class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
960
- onClick=${handleUnlinkClick}
961
- >
962
- ${unlinkText}
963
- </button>
964
-
965
- <button
966
- class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
967
- onClick=${handleEditClick}
968
- >
969
- ${editProcessLinkText}
970
- </button>
971
- </div>
972
- </div>`;
1148
+ ${linkedButtons}
1149
+ </div>`);
973
1150
  }
974
1151
  const pluginActionKey = processLink?.pluginActionDefinitionKey;
975
1152
  const pluginActionTranslation = pluginTranslationService.instantByPluginActionKey(pluginActionKey);
976
1153
  const pluginTitleTranslation = pluginTranslationService.instantPluginTitleByPluginActionKey(pluginActionKey);
977
1154
  if (pluginActionKey) {
978
- return html `<div class="process-link-properties-panel">
979
- <div class="process-link-properties-panel__header">
980
- <span class="process-link-properties-panel__title-container">
981
- <span class="process-link-properties-panel__title">${pluginTitleTranslation}</span>
1155
+ return wrapEntry(html `<div class="process-link-properties-panel">
1156
+ <div class="process-link-properties-panel__header">
1157
+ <span class="process-link-properties-panel__title-container">
1158
+ <span class="process-link-properties-panel__title">${pluginTitleTranslation}</span>
982
1159
 
983
- <span class="process-link-properties-panel__title">${pluginActionTranslation}</span>
984
- </span>
985
-
986
- <cds-tag
987
- class="cds--tag cds--tag--purple cds--tag--md cds--layout--size-md cds-tag--no-margin"
988
- ><span class="cds--tag__label">
989
- ${translateService.instant('processLinkType.plugin')}
1160
+ <span class="process-link-properties-panel__title">${pluginActionTranslation}</span>
990
1161
  </span>
991
- </cds-tag>
992
- </div>
993
1162
 
994
- <div class="process-link-properties-panel__buttons">
995
- <button
996
- class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
997
- onClick=${handleUnlinkClick}
998
- >
999
- ${unlinkText}
1000
- </button>
1163
+ <cds-tag
1164
+ class="cds--tag cds--tag--purple cds--tag--md cds--layout--size-md cds-tag--no-margin"
1165
+ ><span class="cds--tag__label">
1166
+ ${translateService.instant('processLinkType.plugin')}
1167
+ </span>
1168
+ </cds-tag>
1169
+ </div>
1001
1170
 
1002
- <button
1003
- class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1004
- onClick=${handleEditClick}
1005
- >
1006
- ${editProcessLinkText}
1007
- </button>
1008
- </div>
1009
- </div>`;
1171
+ ${linkedButtons}
1172
+ </div>`);
1010
1173
  }
1011
1174
  const uiComponentKey = processLink?.componentKey;
1012
1175
  if (uiComponentKey) {
1013
- return html `<div class="process-link-properties-panel">
1014
- <div class="process-link-properties-panel__header">
1015
- <span class="process-link-properties-panel__title">${uiComponentKey}</span>
1176
+ return wrapEntry(html `<div class="process-link-properties-panel">
1177
+ <div class="process-link-properties-panel__header">
1178
+ <span class="process-link-properties-panel__title">${uiComponentKey}</span>
1016
1179
 
1017
- <cds-tag
1018
- class="cds--tag cds--tag--magenta cds--tag--md cds--layout--size-md cds-tag--no-margin"
1019
- ><span class="cds--tag__label">
1020
- ${translateService.instant('processLinkType.ui-component')}
1021
- </span>
1022
- </cds-tag>
1023
- </div>
1180
+ <cds-tag
1181
+ class="cds--tag cds--tag--magenta cds--tag--md cds--layout--size-md cds-tag--no-margin"
1182
+ ><span class="cds--tag__label">
1183
+ ${translateService.instant('processLinkType.ui-component')}
1184
+ </span>
1185
+ </cds-tag>
1186
+ </div>
1024
1187
 
1025
- <div class="process-link-properties-panel__buttons">
1026
- <button
1027
- class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
1028
- onClick=${handleUnlinkClick}
1188
+ ${linkedButtons}
1189
+ </div>`);
1190
+ }
1191
+ const genericLinkedPanel = html `<div class="process-link-properties-panel">
1192
+ ${linkedButtons}
1193
+ </div>`;
1194
+ const genericCreatePanel = html `<div class="process-link-properties-panel">
1195
+ <div class="process-link-properties-panel__buttons">
1196
+ <button
1197
+ class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1198
+ data-test-id=${PROCESS_LINK_PANEL_TEST_IDS.createButton}
1199
+ onClick=${handleCreateClick}
1200
+ >
1201
+ ${createText}
1202
+ </button>
1203
+ </div>
1204
+ </div>`;
1205
+ return wrapEntry(processLink ? genericLinkedPanel : genericCreatePanel);
1206
+ };
1207
+ const ValidationErrorsElement = (props) => {
1208
+ const getErrorMessage = (error) => {
1209
+ if (error.errorCode) {
1210
+ const translationKey = `processManagement.validationErrorCodes.${error.errorCode}`;
1211
+ const translated = props.translateService.instant(translationKey, {
1212
+ expression: error.expression ?? '',
1213
+ });
1214
+ if (translated !== translationKey) {
1215
+ return translated;
1216
+ }
1217
+ }
1218
+ return error.reason;
1219
+ };
1220
+ return html `<div class="validation-errors-panel">
1221
+ ${props.errors.map(error => html `<div
1222
+ class="validation-errors-panel__item${error.severity === 'WARNING' ? ' warning' : ''}"
1029
1223
  >
1030
- ${unlinkText}
1031
- </button>
1224
+ <span
1225
+ class="validation-errors-panel__icon${error.severity === 'WARNING' ? ' warning' : ''}"
1226
+ >!</span
1227
+ >
1228
+ <span
1229
+ class="validation-errors-panel__reason${error.severity === 'WARNING' ? ' warning' : ''}"
1230
+ >${getErrorMessage(error)}</span
1231
+ >
1232
+ </div>`)}
1233
+ </div>`;
1234
+ };
1235
+ const AutofilledNotificationElement = (props) => {
1236
+ const handleDismiss = (event) => {
1237
+ const processDefinitionId = props.processManagementEditorService.selectionProcessDefinition?.id;
1238
+ if (processDefinitionId) {
1239
+ props.processLinkService.deleteAutofill(processDefinitionId, props.activityId).subscribe();
1240
+ }
1241
+ props.processManagementEditorService.dismissAutofill(props.activityId);
1242
+ const target = event.currentTarget;
1243
+ const panel = target.closest('.autofilled-notification-panel');
1244
+ if (panel) {
1245
+ panel.style.display = 'none';
1246
+ }
1247
+ };
1248
+ return html `<div class="autofilled-notification-panel">
1249
+ <span class="autofilled-notification-panel__icon">!</span>
1250
+ <span class="autofilled-notification-panel__message">
1251
+ ${props.translateService.instant('processManagement.autofilled.sidebarMessage')}
1252
+ </span>
1253
+ <button class="autofilled-notification-panel__dismiss" onClick=${handleDismiss}>×</button>
1254
+ </div>`;
1255
+ };
1256
+ const ValtimoPropertiesProviderModule = {
1257
+ __init__: ['customPropertiesProvider'],
1258
+ customPropertiesProvider: ['type', ValtimoPropertiesProvider],
1259
+ };
1032
1260
 
1033
- <button
1034
- class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1035
- onClick=${handleEditClick}
1036
- >
1037
- ${editProcessLinkText}
1038
- </button>
1261
+ /*
1262
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
1263
+ *
1264
+ * Licensed under EUPL, Version 1.2 (the "License");
1265
+ * you may not use this file except in compliance with the License.
1266
+ * You may obtain a copy of the License at
1267
+ *
1268
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
1269
+ *
1270
+ * Unless required by applicable law or agreed to in writing, software
1271
+ * distributed under the License is distributed on an "AS IS" basis,
1272
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1273
+ * See the License for the specific language governing permissions and
1274
+ * limitations under the License.
1275
+ */
1276
+ const READ_ONLY_PRIORITY = 1;
1277
+ const removeEditActions = (node) => {
1278
+ if (!node || typeof node !== 'object')
1279
+ return;
1280
+ if (node.add)
1281
+ node.add = null;
1282
+ if (node.remove)
1283
+ node.remove = null;
1284
+ if (Array.isArray(node.entries)) {
1285
+ node.entries.forEach((entry) => removeEditActions(entry));
1286
+ }
1287
+ if (Array.isArray(node.items)) {
1288
+ node.items.forEach((item) => removeEditActions(item));
1289
+ }
1290
+ };
1291
+ class ReadOnlyPropertiesProvider {
1292
+ static { this.$inject = ['propertiesPanel']; }
1293
+ constructor(propertiesPanel) {
1294
+ propertiesPanel.registerProvider(READ_ONLY_PRIORITY, this);
1295
+ }
1296
+ getGroups() {
1297
+ return (groups) => {
1298
+ groups.forEach(group => removeEditActions(group));
1299
+ return groups;
1300
+ };
1301
+ }
1302
+ }
1303
+ const ENTRY = '.bio-properties-panel-entry';
1304
+ const READ_ONLY_INPUT_TYPES = ['email', 'number', 'password', 'search', 'tel', 'text', 'url'];
1305
+ const makeControlsReadOnly = (container) => {
1306
+ container.querySelectorAll(`${ENTRY} input`).forEach(input => {
1307
+ if (READ_ONLY_INPUT_TYPES.includes(input.type)) {
1308
+ input.readOnly = true;
1309
+ }
1310
+ else {
1311
+ input.disabled = true;
1312
+ }
1313
+ });
1314
+ container
1315
+ .querySelectorAll(`${ENTRY} textarea`)
1316
+ .forEach(textarea => (textarea.readOnly = true));
1317
+ container
1318
+ .querySelectorAll(`${ENTRY} select, ${ENTRY} button`)
1319
+ .forEach(control => (control.disabled = true));
1320
+ container
1321
+ .querySelectorAll(`${ENTRY} [contenteditable="true"]`)
1322
+ .forEach(editor => editor.setAttribute('contenteditable', 'false'));
1323
+ };
1324
+ class ReadOnlyPropertiesPanel {
1325
+ static { this.$inject = ['config.propertiesPanel', 'eventBus']; }
1326
+ constructor(config, eventBus) {
1327
+ this._observer = null;
1328
+ const parent = config?.parent;
1329
+ if (!parent)
1330
+ return;
1331
+ this._observer = new MutationObserver(() => makeControlsReadOnly(parent));
1332
+ this._observer.observe(parent, { childList: true, subtree: true });
1333
+ eventBus.on('diagram.destroy', () => this._observer?.disconnect());
1334
+ }
1335
+ }
1336
+ const ReadOnlyPropertiesPanelModule = {
1337
+ __init__: ['readOnlyPropertiesProvider', 'readOnlyPropertiesPanel'],
1338
+ readOnlyPropertiesProvider: ['type', ReadOnlyPropertiesProvider],
1339
+ readOnlyPropertiesPanel: ['type', ReadOnlyPropertiesPanel],
1340
+ };
1341
+
1342
+ /*
1343
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
1344
+ *
1345
+ * Licensed under EUPL, Version 1.2 (the "License");
1346
+ * you may not use this file except in compliance with the License.
1347
+ * You may obtain a copy of the License at
1348
+ *
1349
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
1350
+ *
1351
+ * Unless required by applicable law or agreed to in writing, software
1352
+ * distributed under the License is distributed on an "AS IS" basis,
1353
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1354
+ * See the License for the specific language governing permissions and
1355
+ * limitations under the License.
1356
+ */
1357
+ class ExpressionAutocomplete {
1358
+ static { this.$inject = ['eventBus']; }
1359
+ constructor(eventBus) {
1360
+ this.processBeans = [];
1361
+ this.panelContainer = null;
1362
+ this.stylesInjected = false;
1363
+ this.observer = null;
1364
+ this.documentClickHandlers = [];
1365
+ this.injectStyles();
1366
+ eventBus.on('diagram.destroy', () => {
1367
+ this.dispose();
1368
+ });
1369
+ }
1370
+ dispose() {
1371
+ this.cleanupClickHandlers();
1372
+ if (this.observer) {
1373
+ this.observer.disconnect();
1374
+ this.observer = null;
1375
+ }
1376
+ this.panelContainer = null;
1377
+ }
1378
+ cleanupClickHandlers() {
1379
+ for (const handler of this.documentClickHandlers) {
1380
+ document.removeEventListener('click', handler);
1381
+ }
1382
+ this.documentClickHandlers = [];
1383
+ }
1384
+ setProcessBeans(beans) {
1385
+ this.processBeans = beans;
1386
+ }
1387
+ setPanelContainer(container) {
1388
+ this.panelContainer = container;
1389
+ this.startObserving();
1390
+ setTimeout(() => this.scanAndAttach(), 100);
1391
+ }
1392
+ startObserving() {
1393
+ if (!this.panelContainer || this.observer)
1394
+ return;
1395
+ this.observer = new MutationObserver(() => {
1396
+ this.scanAndAttach();
1397
+ });
1398
+ this.observer.observe(this.panelContainer, {
1399
+ childList: true,
1400
+ subtree: true,
1401
+ });
1402
+ }
1403
+ scanAndAttach() {
1404
+ if (!this.panelContainer)
1405
+ return;
1406
+ const allInputs = this.panelContainer.querySelectorAll('input, textarea');
1407
+ allInputs.forEach(input => {
1408
+ if (input.dataset.autocompleteAttached)
1409
+ return;
1410
+ if (this.isExpressionField(input)) {
1411
+ this.attachAutocomplete(input);
1412
+ input.dataset.autocompleteAttached = 'true';
1413
+ }
1414
+ });
1415
+ }
1416
+ isExpressionField(input) {
1417
+ const type = input.type?.toLowerCase();
1418
+ if (input.tagName !== 'TEXTAREA' && !(input.tagName === 'INPUT' && (!type || type === 'text'))) {
1419
+ return false;
1420
+ }
1421
+ if (input.closest('.expression-editor-simple')) {
1422
+ return false;
1423
+ }
1424
+ const name = (input.name || input.id || '').toLowerCase();
1425
+ if (name.includes('resultvariable') || name.includes('result-variable')) {
1426
+ return false;
1427
+ }
1428
+ if (name.includes('expression') || name.includes('delegateexpression')) {
1429
+ return true;
1430
+ }
1431
+ const entry = input.closest('.bio-properties-panel-entry');
1432
+ if (entry) {
1433
+ const entryId = (entry.getAttribute('data-entry-id') || '').toLowerCase();
1434
+ if (entryId.includes('resultvariable') || entryId.includes('result-variable')) {
1435
+ return false;
1436
+ }
1437
+ if (entryId.includes('expression')) {
1438
+ return true;
1439
+ }
1440
+ }
1441
+ const label = input.closest('.bio-properties-panel-entry')?.querySelector('label');
1442
+ if (label) {
1443
+ const labelText = label.textContent?.toLowerCase() || '';
1444
+ if (labelText.includes('result variable') || labelText.includes('resultvariable')) {
1445
+ return false;
1446
+ }
1447
+ if (labelText.includes('expression') || labelText.includes('delegate')) {
1448
+ return true;
1449
+ }
1450
+ }
1451
+ return false;
1452
+ }
1453
+ attachAutocomplete(input) {
1454
+ this.wrapWithEditor(input);
1455
+ }
1456
+ // --- Expression Editor (Dropdown/Manual toggle) ---
1457
+ wrapWithEditor(input) {
1458
+ const wrapper = document.createElement('div');
1459
+ wrapper.className = 'expression-editor-wrapper';
1460
+ const parsed = this.parseExpression(input.value);
1461
+ const canUseDropdown = !input.value || (parsed.beanName !== null && parsed.methodName !== null);
1462
+ const initialMode = canUseDropdown ? 'simple' : 'technical';
1463
+ wrapper.dataset.mode = initialMode;
1464
+ input.parentNode?.insertBefore(wrapper, input);
1465
+ const toggle = document.createElement('div');
1466
+ toggle.className = 'expression-editor-toggle';
1467
+ toggle.innerHTML = `
1468
+ <button type="button" class="expression-editor-toggle__btn${initialMode === 'simple' ? ' active' : ''}" data-mode="simple">Dropdown</button>
1469
+ <button type="button" class="expression-editor-toggle__btn${initialMode === 'technical' ? ' active' : ''}" data-mode="technical">Manual</button>
1470
+ `;
1471
+ const technicalContainer = document.createElement('div');
1472
+ technicalContainer.className = 'expression-editor-technical';
1473
+ technicalContainer.appendChild(input);
1474
+ technicalContainer.style.display = initialMode === 'technical' ? 'block' : 'none';
1475
+ const simpleContainer = document.createElement('div');
1476
+ simpleContainer.className = 'expression-editor-simple';
1477
+ simpleContainer.style.display = initialMode === 'simple' ? 'block' : 'none';
1478
+ wrapper.appendChild(toggle);
1479
+ wrapper.appendChild(technicalContainer);
1480
+ wrapper.appendChild(simpleContainer);
1481
+ toggle.querySelectorAll('button').forEach(btn => {
1482
+ btn.addEventListener('click', e => {
1483
+ e.preventDefault();
1484
+ this.setEditorMode(wrapper, input, btn.dataset.mode || 'technical');
1485
+ });
1486
+ });
1487
+ if (initialMode === 'simple') {
1488
+ this.renderSimpleMode(simpleContainer, input);
1489
+ }
1490
+ }
1491
+ setEditorMode(wrapper, input, mode) {
1492
+ wrapper.dataset.mode = mode;
1493
+ wrapper.querySelectorAll('.expression-editor-toggle__btn').forEach(btn => {
1494
+ btn.classList.toggle('active', btn.dataset.mode === mode);
1495
+ });
1496
+ const technicalContainer = wrapper.querySelector('.expression-editor-technical');
1497
+ const simpleContainer = wrapper.querySelector('.expression-editor-simple');
1498
+ const invalidArgsStr = wrapper.dataset.invalidArgs;
1499
+ const invalidArgs = invalidArgsStr ? JSON.parse(invalidArgsStr) : [];
1500
+ if (mode === 'simple') {
1501
+ technicalContainer.style.display = 'none';
1502
+ simpleContainer.style.display = 'block';
1503
+ input.classList.remove('validation-error-param');
1504
+ this.renderSimpleMode(simpleContainer, input);
1505
+ if (invalidArgs.length > 0) {
1506
+ setTimeout(() => {
1507
+ for (const idx of invalidArgs) {
1508
+ const paramInput = simpleContainer.querySelector(`.expression-editor-param input[data-param-index="${idx}"]`);
1509
+ if (paramInput) {
1510
+ paramInput.classList.add('validation-error-param');
1511
+ }
1512
+ }
1513
+ }, 0);
1514
+ }
1515
+ }
1516
+ else {
1517
+ technicalContainer.style.display = 'block';
1518
+ simpleContainer.style.display = 'none';
1519
+ if (invalidArgs.length > 0) {
1520
+ input.classList.add('validation-error-param');
1521
+ }
1522
+ setTimeout(() => input.blur(), 0);
1523
+ }
1524
+ }
1525
+ renderSimpleMode(container, input) {
1526
+ this.cleanupClickHandlers();
1527
+ const parsed = this.parseExpression(input.value);
1528
+ container.innerHTML = `
1529
+ <div class="expression-editor-field" data-container="bean">
1530
+ <label>Service</label>
1531
+ </div>
1532
+ <div class="expression-editor-field" data-container="method"></div>
1533
+ <div data-container="params"></div>
1534
+ `;
1535
+ const beanContainer = container.querySelector('[data-container="bean"]');
1536
+ this.renderCustomSelect(beanContainer, this.processBeans.map(b => ({
1537
+ value: b.name,
1538
+ label: this.camelCaseToReadable(b.name),
1539
+ description: b.description,
1540
+ })), parsed.beanName, 'Select service...', 'bean', beanName => {
1541
+ this.renderMethodDropdown(container, beanName, null, input);
1542
+ this.renderParameterInputs(container, beanName, null, [], input);
1543
+ this.buildAndSetExpression(container, input);
1544
+ });
1545
+ if (parsed.beanName) {
1546
+ this.renderMethodDropdown(container, parsed.beanName, parsed.methodName, input);
1547
+ }
1548
+ if (parsed.beanName && parsed.methodName) {
1549
+ this.renderParameterInputs(container, parsed.beanName, parsed.methodName, parsed.parameters, input);
1550
+ }
1551
+ }
1552
+ renderCustomSelect(container, options, selectedValue, placeholder, dataField, onChange) {
1553
+ const wrapper = document.createElement('div');
1554
+ wrapper.className = 'expression-editor-custom-select';
1555
+ wrapper.dataset.field = dataField;
1556
+ wrapper.dataset.value = selectedValue || '';
1557
+ const nativeSelect = document.createElement('select');
1558
+ nativeSelect.className = 'bio-properties-panel-input';
1559
+ nativeSelect.innerHTML =
1560
+ `<option value="" disabled ${!selectedValue ? 'selected' : ''}>${this.escapeHtml(placeholder)}</option>` +
1561
+ options
1562
+ .map(opt => `<option value="${this.escapeAttr(opt.value)}" ${opt.value === selectedValue ? 'selected' : ''}>${this.escapeHtml(opt.label)}</option>`)
1563
+ .join('');
1564
+ const selectedOption = options.find(o => o.value === selectedValue);
1565
+ if (selectedOption?.description) {
1566
+ nativeSelect.title = selectedOption.description;
1567
+ }
1568
+ const dropdown = document.createElement('div');
1569
+ dropdown.className = 'expression-editor-custom-select__dropdown';
1570
+ dropdown.style.display = 'none';
1571
+ let selectedIndex = options.findIndex(o => o.value === selectedValue);
1572
+ if (selectedIndex < 0)
1573
+ selectedIndex = 0;
1574
+ const renderItems = () => {
1575
+ dropdown.innerHTML = options
1576
+ .map((opt, i) => `
1577
+ <div class="expression-editor-custom-select__item${opt.value === selectedValue ? ' selected' : ''}" data-index="${i}" data-value="${this.escapeAttr(opt.value)}">
1578
+ <div class="expression-editor-custom-select__label">${this.escapeHtml(opt.label)}</div>
1579
+ ${opt.description ? `<div class="expression-editor-custom-select__desc">${this.escapeHtml(opt.description)}</div>` : ''}
1580
+ </div>
1581
+ `)
1582
+ .join('');
1583
+ dropdown.querySelectorAll('.expression-editor-custom-select__item').forEach(item => {
1584
+ item.addEventListener('click', e => {
1585
+ e.stopPropagation();
1586
+ const value = item.dataset.value;
1587
+ const selected = options.find(o => o.value === value);
1588
+ wrapper.dataset.value = value;
1589
+ nativeSelect.value = value;
1590
+ nativeSelect.title = selected?.description || '';
1591
+ dropdown.style.display = 'none';
1592
+ onChange(value);
1593
+ });
1594
+ });
1595
+ };
1596
+ renderItems();
1597
+ nativeSelect.addEventListener('mousedown', e => {
1598
+ e.preventDefault();
1599
+ nativeSelect.focus();
1600
+ const isOpen = dropdown.style.display !== 'none';
1601
+ dropdown.style.display = isOpen ? 'none' : 'block';
1602
+ });
1603
+ nativeSelect.addEventListener('keydown', e => {
1604
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
1605
+ e.preventDefault();
1606
+ if (dropdown.style.display === 'none') {
1607
+ dropdown.style.display = 'block';
1608
+ }
1609
+ const delta = e.key === 'ArrowDown' ? 1 : -1;
1610
+ selectedIndex = Math.max(0, Math.min(options.length - 1, selectedIndex + delta));
1611
+ dropdown.querySelectorAll('.expression-editor-custom-select__item').forEach((item, i) => {
1612
+ item.classList.toggle('highlighted', i === selectedIndex);
1613
+ });
1614
+ dropdown.children[selectedIndex]?.scrollIntoView({ block: 'nearest' });
1615
+ }
1616
+ else if (e.key === 'Enter' && dropdown.style.display !== 'none') {
1617
+ e.preventDefault();
1618
+ const opt = options[selectedIndex];
1619
+ if (opt) {
1620
+ wrapper.dataset.value = opt.value;
1621
+ nativeSelect.value = opt.value;
1622
+ nativeSelect.title = opt.description || '';
1623
+ dropdown.style.display = 'none';
1624
+ onChange(opt.value);
1625
+ }
1626
+ }
1627
+ else if (e.key === 'Escape') {
1628
+ dropdown.style.display = 'none';
1629
+ }
1630
+ else if (e.key === ' ' && dropdown.style.display === 'none') {
1631
+ e.preventDefault();
1632
+ dropdown.style.display = 'block';
1633
+ }
1634
+ });
1635
+ const closeHandler = (e) => {
1636
+ if (!wrapper.contains(e.target)) {
1637
+ dropdown.style.display = 'none';
1638
+ }
1639
+ };
1640
+ document.addEventListener('click', closeHandler);
1641
+ this.documentClickHandlers.push(closeHandler);
1642
+ wrapper.appendChild(nativeSelect);
1643
+ wrapper.appendChild(dropdown);
1644
+ container.appendChild(wrapper);
1645
+ }
1646
+ renderMethodDropdown(container, beanName, selectedMethod, input) {
1647
+ const methodContainer = container.querySelector('[data-container="method"]');
1648
+ if (!methodContainer)
1649
+ return;
1650
+ const bean = this.processBeans.find(b => b.name === beanName);
1651
+ if (!bean) {
1652
+ methodContainer.innerHTML = '';
1653
+ return;
1654
+ }
1655
+ methodContainer.innerHTML = '<label>Method</label>';
1656
+ this.renderCustomSelect(methodContainer, bean.methods.map(m => ({
1657
+ value: m.name,
1658
+ label: this.camelCaseToReadable(m.name),
1659
+ description: m.description,
1660
+ })), selectedMethod, 'Select method...', 'method', methodName => {
1661
+ this.renderParameterInputs(container, beanName, methodName, [], input);
1662
+ this.buildAndSetExpression(container, input);
1663
+ });
1664
+ }
1665
+ renderParameterInputs(container, beanName, methodName, values, input) {
1666
+ const paramsContainer = container.querySelector('[data-container="params"]');
1667
+ if (!paramsContainer)
1668
+ return;
1669
+ if (!methodName) {
1670
+ paramsContainer.innerHTML = '';
1671
+ return;
1672
+ }
1673
+ const bean = this.processBeans.find(b => b.name === beanName);
1674
+ const method = bean?.methods.find(m => m.name === methodName);
1675
+ if (!method || method.parameters.length === 0) {
1676
+ paramsContainer.innerHTML = '';
1677
+ return;
1678
+ }
1679
+ paramsContainer.innerHTML = method.parameters
1680
+ .map((p, i) => `
1681
+ <div class="expression-editor-param">
1682
+ <label>${this.escapeHtml(p.name)} <span class="type">(${this.escapeHtml(p.type)})</span></label>
1683
+ <input type="text" class="expression-editor-input" data-param-index="${i}" value="${this.escapeAttr(values[i] || '')}">
1039
1684
  </div>
1040
- </div>`;
1685
+ `)
1686
+ .join('');
1687
+ paramsContainer.querySelectorAll('input').forEach(paramInput => {
1688
+ paramInput.addEventListener('input', () => {
1689
+ this.buildAndSetExpression(container, input);
1690
+ });
1691
+ });
1041
1692
  }
1042
- const genericLinkedPanel = html `<div class="process-link-properties-panel">
1043
- <div class="process-link-properties-panel__buttons">
1044
- <button
1045
- class="cds--btn cds--btn--danger cds--btn--sm cds--layout--side-md"
1046
- onClick=${handleUnlinkClick}
1047
- >
1048
- ${unlinkText}
1049
- </button>
1050
-
1051
- <button
1052
- class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1053
- onClick=${handleEditClick}
1054
- >
1055
- ${editProcessLinkText}
1056
- </button>
1057
- </div>
1058
- </div>`;
1059
- const genericCreatePanel = html `<div class="process-link-properties-panel">
1060
- <div class="process-link-properties-panel__buttons">
1061
- <button
1062
- class="cds--btn cds--btn--primary cds--btn--sm cds--layout--size-md"
1063
- onClick=${handleCreateClick}
1064
- >
1065
- ${createText}
1066
- </button>
1067
- </div>
1068
- </div>`;
1069
- return processLink ? genericLinkedPanel : genericCreatePanel;
1070
- };
1071
- const ValidationErrorsElement = (props) => {
1072
- const getErrorMessage = (error) => {
1073
- if (error.errorCode) {
1074
- const translationKey = `processManagement.expressionErrors.${error.errorCode}`;
1075
- const translated = props.translateService.instant(translationKey, { expression: error.expression ? `'${error.expression}'` : '' });
1076
- if (translated !== translationKey) {
1077
- return translated;
1693
+ buildAndSetExpression(simpleContainer, input) {
1694
+ const beanSelect = simpleContainer.querySelector('[data-field="bean"]');
1695
+ const methodSelect = simpleContainer.querySelector('[data-field="method"]');
1696
+ const paramInputs = simpleContainer.querySelectorAll('[data-param-index]');
1697
+ const bean = beanSelect?.dataset.value;
1698
+ const method = methodSelect?.dataset.value;
1699
+ if (!bean || !method) {
1700
+ input.value = '';
1701
+ input.dispatchEvent(new Event('input', { bubbles: true }));
1702
+ return;
1703
+ }
1704
+ const params = Array.from(paramInputs).map(i => i.value);
1705
+ input.value = `\${${bean}.${method}(${params.join(', ')})}`;
1706
+ input.dispatchEvent(new Event('input', { bubbles: true }));
1707
+ }
1708
+ parseExpression(expr) {
1709
+ const match = expr.match(/^\$\{([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)\((.*)\)\}$/);
1710
+ if (!match)
1711
+ return { beanName: null, methodName: null, parameters: [] };
1712
+ return {
1713
+ beanName: match[1],
1714
+ methodName: match[2],
1715
+ parameters: this.parseParameters(match[3]),
1716
+ };
1717
+ }
1718
+ parseParameters(paramsStr) {
1719
+ const params = [];
1720
+ let current = '';
1721
+ let depth = 0;
1722
+ let inString = false;
1723
+ let stringChar = '';
1724
+ for (const char of paramsStr) {
1725
+ if (!inString && (char === '"' || char === "'")) {
1726
+ inString = true;
1727
+ stringChar = char;
1728
+ current += char;
1729
+ }
1730
+ else if (inString && char === stringChar) {
1731
+ inString = false;
1732
+ current += char;
1733
+ }
1734
+ else if (!inString && (char === '(' || char === '[')) {
1735
+ depth++;
1736
+ current += char;
1737
+ }
1738
+ else if (!inString && (char === ')' || char === ']')) {
1739
+ depth--;
1740
+ current += char;
1741
+ }
1742
+ else if (!inString && char === ',' && depth === 0) {
1743
+ params.push(current.trim());
1744
+ current = '';
1745
+ }
1746
+ else {
1747
+ current += char;
1078
1748
  }
1079
1749
  }
1080
- return error.reason;
1081
- };
1082
- return html `<div class="validation-errors-panel">
1083
- ${props.errors.map(error => html `<div class="validation-errors-panel__item${error.severity === 'WARNING' ? ' warning' : ''}">
1084
- <span class="validation-errors-panel__icon${error.severity === 'WARNING' ? ' warning' : ''}">!</span>
1085
- <span class="validation-errors-panel__reason${error.severity === 'WARNING' ? ' warning' : ''}">${getErrorMessage(error)}</span>
1086
- </div>`)}
1087
- </div>`;
1088
- };
1089
- const ValtimoPropertiesProviderModule = {
1090
- __init__: ['customPropertiesProvider'],
1091
- customPropertiesProvider: ['type', ValtimoPropertiesProvider],
1750
+ if (current.trim())
1751
+ params.push(current.trim());
1752
+ return params;
1753
+ }
1754
+ escapeHtml(text) {
1755
+ const div = document.createElement('div');
1756
+ div.textContent = text;
1757
+ return div.innerHTML;
1758
+ }
1759
+ escapeAttr(text) {
1760
+ return text
1761
+ .replace(/&/g, '&amp;')
1762
+ .replace(/"/g, '&quot;')
1763
+ .replace(/'/g, '&#39;')
1764
+ .replace(/</g, '&lt;')
1765
+ .replace(/>/g, '&gt;');
1766
+ }
1767
+ camelCaseToReadable(text) {
1768
+ return text
1769
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
1770
+ .replace(/^./, str => str.toUpperCase());
1771
+ }
1772
+ injectStyles() {
1773
+ if (this.stylesInjected)
1774
+ return;
1775
+ this.stylesInjected = true;
1776
+ const style = document.createElement('style');
1777
+ style.textContent = `
1778
+ /* Expression Editor Toggle UI */
1779
+ .expression-editor-wrapper {
1780
+ width: 100%;
1781
+ }
1782
+ .expression-editor-toggle {
1783
+ display: flex;
1784
+ gap: 0;
1785
+ margin-bottom: 8px;
1786
+ }
1787
+ .expression-editor-toggle__btn {
1788
+ flex: 1;
1789
+ padding: 4px 8px;
1790
+ border: 1px solid #ccc;
1791
+ background: #fafafa;
1792
+ color: #333;
1793
+ cursor: pointer;
1794
+ font-size: 13px;
1795
+ font-family: inherit;
1796
+ }
1797
+ .expression-editor-toggle__btn:first-child {
1798
+ border-radius: 2px 0 0 2px;
1799
+ }
1800
+ .expression-editor-toggle__btn:last-child {
1801
+ border-radius: 0 2px 2px 0;
1802
+ border-left: none;
1803
+ }
1804
+ .expression-editor-toggle__btn.active {
1805
+ background: #2b79bd;
1806
+ color: white;
1807
+ border-color: #2b79bd;
1808
+ }
1809
+ .expression-editor-toggle__btn:hover:not(.active) {
1810
+ background: #fff;
1811
+ }
1812
+ .expression-editor-simple {
1813
+ display: flex;
1814
+ flex-direction: column;
1815
+ gap: 6px;
1816
+ }
1817
+ .expression-editor-field label,
1818
+ .expression-editor-param > label {
1819
+ display: block;
1820
+ font-size: var(--text-size-small, 12px);
1821
+ color: var(--label-color, #666);
1822
+ margin-bottom: 2px;
1823
+ }
1824
+ .expression-editor-field label .type,
1825
+ .expression-editor-param label .type {
1826
+ color: var(--description-color, #808080);
1827
+ }
1828
+ .expression-editor-select,
1829
+ .expression-editor-input {
1830
+ display: block;
1831
+ width: 100%;
1832
+ padding: 3px 6px 2px;
1833
+ border: 1px solid #ccc;
1834
+ border-radius: 2px;
1835
+ font-size: 14px;
1836
+ background-color: #fafafa;
1837
+ font-family: inherit;
1838
+ box-sizing: border-box;
1839
+ }
1840
+ .expression-editor-select:focus,
1841
+ .expression-editor-input:focus {
1842
+ outline: none;
1843
+ background-color: hsl(205, 100%, 95%);
1844
+ border-color: hsl(205, 100%, 50%);
1845
+ }
1846
+ /* Override bpmn-js input styles to prevent focus-within highlighting */
1847
+ .expression-editor-technical input,
1848
+ .expression-editor-technical textarea {
1849
+ background-color: #fafafa !important;
1850
+ border-color: #ccc !important;
1851
+ }
1852
+ /* Override native select and input styles in simple mode */
1853
+ .expression-editor-simple select.bio-properties-panel-input,
1854
+ .expression-editor-simple .expression-editor-input {
1855
+ background-color: #fafafa !important;
1856
+ border-color: #ccc !important;
1857
+ }
1858
+ .expression-editor-simple select.bio-properties-panel-input:focus,
1859
+ .expression-editor-simple .expression-editor-input:focus {
1860
+ background-color: hsl(205, 100%, 95%) !important;
1861
+ border-color: hsl(205, 100%, 50%) !important;
1862
+ }
1863
+ .expression-editor-technical input:focus,
1864
+ .expression-editor-technical textarea:focus {
1865
+ background-color: hsl(205, 100%, 95%) !important;
1866
+ border-color: hsl(205, 100%, 50%) !important;
1867
+ }
1868
+ .expression-editor-param {
1869
+ padding-left: 8px;
1870
+ border-left: 2px solid #ccc;
1871
+ margin-top: 4px;
1872
+ }
1873
+ .expression-editor-description {
1874
+ font-size: var(--text-size-small, 12px);
1875
+ color: var(--description-color, #808080);
1876
+ margin-top: 4px;
1877
+ font-style: italic;
1878
+ }
1879
+
1880
+ /* Custom Select (Listbox) */
1881
+ .expression-editor-custom-select {
1882
+ position: relative;
1883
+ width: 100%;
1884
+ }
1885
+ .expression-editor-custom-select select {
1886
+ width: 100%;
1887
+ }
1888
+ .expression-editor-custom-select__dropdown {
1889
+ position: absolute;
1890
+ top: 100%;
1891
+ left: 0;
1892
+ right: 0;
1893
+ max-height: 200px;
1894
+ overflow-y: auto;
1895
+ background: #fff;
1896
+ border: 1px solid #ccc;
1897
+ border-radius: 2px;
1898
+ box-shadow: 0 2px 6px rgba(0,0,0,0.15);
1899
+ z-index: 1000;
1900
+ margin-top: 2px;
1901
+ }
1902
+ .expression-editor-custom-select__item {
1903
+ padding: 8px;
1904
+ cursor: pointer;
1905
+ border-bottom: 1px solid #eee;
1906
+ }
1907
+ .expression-editor-custom-select__item:last-child {
1908
+ border-bottom: none;
1909
+ }
1910
+ .expression-editor-custom-select__item:hover,
1911
+ .expression-editor-custom-select__item.selected,
1912
+ .expression-editor-custom-select__item.highlighted {
1913
+ background: hsl(205, 100%, 95%);
1914
+ }
1915
+ .expression-editor-custom-select__label {
1916
+ font-weight: 500;
1917
+ font-size: 14px;
1918
+ color: #333;
1919
+ }
1920
+ .expression-editor-custom-select__desc {
1921
+ font-size: 11px;
1922
+ color: #666;
1923
+ margin-top: 2px;
1924
+ }
1925
+ `;
1926
+ document.head.appendChild(style);
1927
+ }
1928
+ }
1929
+ const ExpressionAutocompleteModule = {
1930
+ __init__: ['expressionAutocomplete'],
1931
+ expressionAutocomplete: ['type', ExpressionAutocomplete],
1092
1932
  };
1093
1933
 
1094
1934
  /*
@@ -1123,7 +1963,13 @@ const ValtimoPropertiesProviderModule = {
1123
1963
  * limitations under the License.
1124
1964
  */
1125
1965
  class ProcessManagementBuilderComponent {
1126
- constructor(breadcrumbService, iconService, logger, modalService, notificationService, pageHeaderService, pageTitleService, processLinkService, processLinkStateService, processManagementEditorService, processManagementService, processService, route, router, translateService, pluginTranslationService, editPermissionsService, processLinkBuildingBlockApiService) {
1966
+ get activeEditor() {
1967
+ return this._viewerActive ? this._bpmnViewer : this._bpmnModeler;
1968
+ }
1969
+ get loadedEditors() {
1970
+ return [this._bpmnModeler, this._bpmnViewer].filter(editor => !!editor);
1971
+ }
1972
+ constructor(breadcrumbService, iconService, logger, modalService, notificationService, pageHeaderService, pageTitleService, processLinkService, processLinkStateService, processManagementEditorService, processManagementService, processService, route, router, translateService, pluginTranslationService, editPermissionsService, processLinkBuildingBlockApiService, processBeanService) {
1127
1973
  this.breadcrumbService = breadcrumbService;
1128
1974
  this.iconService = iconService;
1129
1975
  this.logger = logger;
@@ -1142,26 +1988,41 @@ class ProcessManagementBuilderComponent {
1142
1988
  this.pluginTranslationService = pluginTranslationService;
1143
1989
  this.editPermissionsService = editPermissionsService;
1144
1990
  this.processLinkBuildingBlockApiService = processLinkBuildingBlockApiService;
1991
+ this.processBeanService = processBeanService;
1145
1992
  this._selectedProcess$ = new BehaviorSubject(null);
1146
1993
  this.loading$ = new BehaviorSubject(true);
1147
1994
  this._validationErrorElementIds = [];
1148
1995
  this._validationHoverHandler = null;
1149
1996
  this._validationOutHandler = null;
1997
+ this._selectionChangedHandler = null;
1998
+ this._fieldInputCleanupFns = [];
1999
+ this._expressionAutocomplete = null;
2000
+ this._activityMarkerElementIds = [];
2001
+ this._activityMarkerUpdateTimeout = null;
2002
+ this._autofilledElements = [];
2003
+ this._autofilledElementIds = [];
2004
+ this._viewerActive = false;
1150
2005
  this.isReadOnlyProcess$ = new BehaviorSubject(false);
1151
2006
  this.isSystemProcess$ = new BehaviorSubject(false);
1152
2007
  this.draft$ = new BehaviorSubject(false);
1153
2008
  this.canInitializeDocument$ = new BehaviorSubject(false);
1154
2009
  this.startableByUser$ = new BehaviorSubject(false);
1155
2010
  this.validationErrors$ = this.processManagementEditorService.validationErrors$;
2011
+ this.$markersVisible = signal(true);
1156
2012
  this.testIds = PROCESS_MANAGEMENT_BUILDER_TEST_IDS;
1157
2013
  this.selectedProcessDefinitionXml$ = this.processManagementEditorService.selectionProcessDefinition$.pipe(filter(selectedProcessDefinition => !!selectedProcessDefinition?.id), distinctUntilChanged((previous, current) => isEqual(previous, current)), tap(selectedProcessDefinition => {
1158
2014
  this.loading$.next(true);
1159
2015
  this.pageTitleService.setCustomPageTitle(selectedProcessDefinition?.name || '-');
1160
2016
  }), switchMap(selectedProcessDefinition => this.processService.getProcessDefinitionXml(selectedProcessDefinition.id)), tap(result => {
1161
2017
  this.cleanUpListenersOnModeler();
1162
- this._bpmnModeler?.importXML(result.bpmn20Xml);
1163
- this._bpmnViewer?.importXML(result.bpmn20Xml);
1164
- this.draft$.next(this.parseDraftFromXml(result.bpmn20Xml));
2018
+ this._autofilledElements = result.autofilledElements ?? [];
2019
+ this.processManagementEditorService.setAutofilledElements(this._autofilledElements);
2020
+ this._bpmnModeler?.importXML(result.bpmn20Xml).then(() => {
2021
+ this.highlightAutofilledElements();
2022
+ });
2023
+ this._bpmnViewer?.importXML(result.bpmn20Xml).then(() => {
2024
+ this.highlightAutofilledElements();
2025
+ });
1165
2026
  this.isReadOnlyProcess$.next(result.readOnly);
1166
2027
  this.isSystemProcess$.next(result.systemProcess);
1167
2028
  this.loading$.next(false);
@@ -1186,6 +2047,10 @@ class ProcessManagementBuilderComponent {
1186
2047
  }
1187
2048
  return this.managementParams$.pipe(switchMap(params => this.editPermissionsService.hasPermissionsToEditBasedOnContext(params, context)));
1188
2049
  }));
2050
+ this.viewerActive$ = combineLatest([
2051
+ this.isReadOnlyProcess$,
2052
+ this.hasEditPermissions$,
2053
+ ]).pipe(map$1(([isReadOnlyProcess, hasEditPermissions]) => isReadOnlyProcess || !hasEditPermissions), distinctUntilChanged());
1189
2054
  this._reload$ = new Subject();
1190
2055
  this.processDefinitionVersions$ = combineLatest([this.editParam$, this.context$, this._reload$.pipe(startWith(null))]).pipe(switchMap(([editParam, context]) => context === 'independent'
1191
2056
  ? this.processManagementService.getUnlinkedProcessDefinitionsByKey(editParam)
@@ -1228,8 +2093,9 @@ class ProcessManagementBuilderComponent {
1228
2093
  if (!activityId || !businessId)
1229
2094
  return;
1230
2095
  this.processManagementEditorService.updateProcessLinksOnIdChange(activityId, businessId);
2096
+ this.scheduleActivityMarkerUpdate();
1231
2097
  };
1232
- this.iconService.registerAll([Deploy16, ListChecked16, Return16]);
2098
+ this.iconService.registerAll([View16, ViewOff16]);
1233
2099
  this.setProcessManagementWindow();
1234
2100
  }
1235
2101
  ngAfterViewInit() {
@@ -1242,10 +2108,16 @@ class ProcessManagementBuilderComponent {
1242
2108
  this.subscribeToProcessLinkUpdateEvents();
1243
2109
  this.subscribeToProcessLinkCreateEvents();
1244
2110
  this.subscribeToProcessLinkDeleteEvents();
2111
+ this.subscribeToAutofillDismissEvents();
2112
+ this.subscribeToViewerActive();
1245
2113
  this.initEditing();
1246
2114
  }
1247
2115
  ngOnDestroy() {
1248
2116
  this.clearValidationErrors();
2117
+ this.clearActivityMarkers();
2118
+ if (this._activityMarkerUpdateTimeout) {
2119
+ clearTimeout(this._activityMarkerUpdateTimeout);
2120
+ }
1249
2121
  this._bpmnModeler?.destroy();
1250
2122
  this._bpmnViewer?.destroy();
1251
2123
  this._subscriptions.unsubscribe();
@@ -1255,19 +2127,50 @@ class ProcessManagementBuilderComponent {
1255
2127
  this.breadcrumbService.clearThirdBreadcrumb();
1256
2128
  this.breadcrumbService.clearFourthBreadcrumb();
1257
2129
  }
1258
- export(isReadOnlyProcess) {
2130
+ exportBpmn(isReadOnlyProcess) {
1259
2131
  (isReadOnlyProcess ? from(this._bpmnViewer.saveXML()) : from(this._bpmnModeler.saveXML()))
1260
2132
  .pipe(take(1))
1261
2133
  .subscribe(result => {
1262
2134
  const file = new Blob([result.xml ?? ''], { type: 'text/xml' });
1263
- const link = document.createElement('a');
1264
- link.download = 'diagram.bpmn';
1265
- link.href = window.URL.createObjectURL(file);
1266
- link.click();
1267
- window.URL.revokeObjectURL(link.href);
1268
- link.remove();
2135
+ const processDefinitionKey = this.processManagementEditorService.selectionProcessDefinition?.key;
2136
+ this.downloadFile(file, processDefinitionKey ? `${processDefinitionKey}.bpmn` : 'diagram.bpmn');
2137
+ });
2138
+ }
2139
+ export() {
2140
+ const processDefinitionId = this.processManagementEditorService.selectionProcessDefinition?.id;
2141
+ if (!processDefinitionId) {
2142
+ this.showNotification('exportError');
2143
+ return;
2144
+ }
2145
+ this.processManagementService
2146
+ .exportProcessDefinition(processDefinitionId)
2147
+ .pipe(take(1))
2148
+ .subscribe({
2149
+ next: response => {
2150
+ const blob = response.body;
2151
+ if (!blob) {
2152
+ this.showNotification('exportError');
2153
+ return;
2154
+ }
2155
+ this.downloadFile(blob, this.getFileName(response));
2156
+ },
2157
+ error: () => {
2158
+ this.showNotification('exportError');
2159
+ },
1269
2160
  });
1270
2161
  }
2162
+ getFileName(response) {
2163
+ const contentDisposition = response.headers.get('Content-Disposition') ?? '';
2164
+ return contentDisposition.split('filename=')[1]?.replace(/"/g, '').trim() || 'process.zip';
2165
+ }
2166
+ downloadFile(file, fileName) {
2167
+ const link = document.createElement('a');
2168
+ link.download = fileName;
2169
+ link.href = window.URL.createObjectURL(file);
2170
+ link.click();
2171
+ window.URL.revokeObjectURL(link.href);
2172
+ link.remove();
2173
+ }
1271
2174
  validateProcessDefinition(isReadOnlyProcess) {
1272
2175
  combineLatest([
1273
2176
  from(isReadOnlyProcess ? this._bpmnViewer.saveXML() : this._bpmnModeler.saveXML()),
@@ -1281,6 +2184,8 @@ class ProcessManagementBuilderComponent {
1281
2184
  ...link,
1282
2185
  processDefinitionId: '-',
1283
2186
  })),
2187
+ canInitializeDocument: this.canInitializeDocument$.getValue(),
2188
+ startableByUser: this.startableByUser$.getValue(),
1284
2189
  });
1285
2190
  }))
1286
2191
  .subscribe({
@@ -1321,6 +2226,7 @@ class ProcessManagementBuilderComponent {
1321
2226
  .subscribe({
1322
2227
  next: context => {
1323
2228
  this.clearValidationErrors();
2229
+ this.processManagementEditorService.clearDismissedAutofills();
1324
2230
  if (context === 'independent') {
1325
2231
  this.reload();
1326
2232
  this.showNotification('success');
@@ -1369,6 +2275,7 @@ class ProcessManagementBuilderComponent {
1369
2275
  .subscribe({
1370
2276
  next: () => {
1371
2277
  this.clearValidationErrors();
2278
+ this.processManagementEditorService.clearDismissedAutofills();
1372
2279
  this.navigateBack('success');
1373
2280
  },
1374
2281
  error: (error) => {
@@ -1400,27 +2307,6 @@ class ProcessManagementBuilderComponent {
1400
2307
  return;
1401
2308
  this.showNotification(notification);
1402
2309
  }
1403
- onValidationErrorClick(elementId) {
1404
- const modeler = this.isReadOnlyProcess$.getValue() ? this._bpmnViewer : this._bpmnModeler;
1405
- const elementRegistry = modeler.get('elementRegistry');
1406
- const canvas = modeler.get('canvas');
1407
- const element = elementRegistry.get(elementId);
1408
- if (!element)
1409
- return;
1410
- try {
1411
- const selection = modeler.get('selection');
1412
- selection?.select?.(element);
1413
- }
1414
- catch {
1415
- // Viewer may not expose the selection service.
1416
- }
1417
- try {
1418
- canvas.scrollToElement(element);
1419
- }
1420
- catch {
1421
- // ignore
1422
- }
1423
- }
1424
2310
  onProcessToggleChange(field, value) {
1425
2311
  if (field === 'draft')
1426
2312
  this.draft$.next(value);
@@ -1440,7 +2326,6 @@ class ProcessManagementBuilderComponent {
1440
2326
  this._pendingDeployAction = null;
1441
2327
  }
1442
2328
  validateAndDeploy(isReadOnlyProcess, deployAction) {
1443
- // Skip validation for draft processes
1444
2329
  if (this.draft$.getValue()) {
1445
2330
  deployAction();
1446
2331
  return;
@@ -1457,27 +2342,25 @@ class ProcessManagementBuilderComponent {
1457
2342
  ...link,
1458
2343
  processDefinitionId: '-',
1459
2344
  })),
2345
+ canInitializeDocument: this.canInitializeDocument$.getValue(),
2346
+ startableByUser: this.startableByUser$.getValue(),
1460
2347
  });
1461
2348
  }))
1462
2349
  .subscribe({
1463
2350
  next: validationResult => {
1464
2351
  if (!validationResult.isValid) {
1465
- // Has errors - show them and block deployment
1466
2352
  this.highlightValidationErrors(validationResult.errors);
1467
2353
  }
1468
2354
  else if (validationResult.hasWarnings) {
1469
- // Only warnings - show confirmation dialog
1470
2355
  this.highlightValidationErrors(validationResult.errors);
1471
2356
  this._pendingDeployAction = deployAction;
1472
2357
  this.showWarningConfirmationModal$.next(true);
1473
2358
  }
1474
2359
  else {
1475
- // No issues - proceed with deployment
1476
2360
  deployAction();
1477
2361
  }
1478
2362
  },
1479
2363
  error: () => {
1480
- // Validation endpoint failed - proceed with deployment anyway (server will validate again)
1481
2364
  deployAction();
1482
2365
  },
1483
2366
  });
@@ -1489,6 +2372,7 @@ class ProcessManagementBuilderComponent {
1489
2372
  processManagementWindow.processManagementEditorService = this.processManagementEditorService;
1490
2373
  processManagementWindow.translateService = this.translateService;
1491
2374
  processManagementWindow.pluginTranslationService = this.pluginTranslationService;
2375
+ processManagementWindow.processLinkService = this.processLinkService;
1492
2376
  }
1493
2377
  showNotification(notification) {
1494
2378
  if (!notification)
@@ -1496,6 +2380,7 @@ class ProcessManagementBuilderComponent {
1496
2380
  let type;
1497
2381
  if (notification === 'alreadyExists' ||
1498
2382
  notification === 'validationError' ||
2383
+ notification === 'exportError' ||
1499
2384
  notification === 'error') {
1500
2385
  type = 'error';
1501
2386
  }
@@ -1557,7 +2442,7 @@ class ProcessManagementBuilderComponent {
1557
2442
  highlightValidationErrors(errors) {
1558
2443
  this.clearValidationErrors();
1559
2444
  this.processManagementEditorService.setValidationErrors(errors);
1560
- const modeler = this.isReadOnlyProcess$.getValue() ? this._bpmnViewer : this._bpmnModeler;
2445
+ const modeler = this.activeEditor;
1561
2446
  const canvas = modeler.get('canvas');
1562
2447
  const overlays = modeler.get('overlays');
1563
2448
  const errorElementIds = new Set();
@@ -1620,6 +2505,10 @@ class ProcessManagementBuilderComponent {
1620
2505
  };
1621
2506
  eventBus.on('element.hover', this._validationHoverHandler);
1622
2507
  eventBus.on('element.out', this._validationOutHandler);
2508
+ this._selectionChangedHandler = (event) => {
2509
+ this.highlightInvalidFieldsInPropertiesPanel(event.newSelection, errors);
2510
+ };
2511
+ eventBus.on('selection.changed', this._selectionChangedHandler);
1623
2512
  const selection = modeler.get('selection');
1624
2513
  const selected = selection.get();
1625
2514
  if (selected?.length > 0) {
@@ -1628,8 +2517,88 @@ class ProcessManagementBuilderComponent {
1628
2517
  selection.select(current);
1629
2518
  }
1630
2519
  }
2520
+ highlightInvalidFieldsInPropertiesPanel(newSelection, errors) {
2521
+ document.querySelectorAll('.validation-error-field').forEach(el => {
2522
+ el.classList.remove('validation-error-field');
2523
+ });
2524
+ document.querySelectorAll('.validation-error-param').forEach(el => {
2525
+ el.classList.remove('validation-error-param');
2526
+ });
2527
+ if (!newSelection?.length)
2528
+ return;
2529
+ const selectedId = newSelection[0]?.id;
2530
+ if (!selectedId)
2531
+ return;
2532
+ const elementErrors = errors.filter(e => e.elementId === selectedId);
2533
+ setTimeout(() => {
2534
+ for (const error of elementErrors) {
2535
+ const listenerEntryId = error.listenerType != null && error.listenerIndex != null
2536
+ ? `${selectedId}-${error.listenerType}-${error.listenerIndex}`
2537
+ : null;
2538
+ if (error.invalidFields) {
2539
+ for (const fieldId of error.invalidFields) {
2540
+ let entry = null;
2541
+ if (listenerEntryId) {
2542
+ const listenerEntry = document.querySelector(`[data-entry-id="${listenerEntryId}"]`);
2543
+ if (listenerEntry) {
2544
+ entry = listenerEntry.querySelector(`[data-entry-id="${fieldId}"]`);
2545
+ }
2546
+ }
2547
+ if (!entry) {
2548
+ entry = document.querySelector(`[data-entry-id="${fieldId}"]`);
2549
+ }
2550
+ if (entry) {
2551
+ entry.classList.add('validation-error-field');
2552
+ const input = entry.querySelector('input, textarea, select');
2553
+ if (input) {
2554
+ const handler = () => {
2555
+ entry.classList.remove('validation-error-field');
2556
+ input.removeEventListener('input', handler);
2557
+ input.removeEventListener('change', handler);
2558
+ };
2559
+ input.addEventListener('input', handler);
2560
+ input.addEventListener('change', handler);
2561
+ this._fieldInputCleanupFns.push(() => {
2562
+ input.removeEventListener('input', handler);
2563
+ input.removeEventListener('change', handler);
2564
+ });
2565
+ }
2566
+ }
2567
+ }
2568
+ }
2569
+ if (error.invalidArguments && error.invalidArguments.length > 0) {
2570
+ let scopeElement = null;
2571
+ if (listenerEntryId) {
2572
+ scopeElement = document.querySelector(`[data-entry-id="${listenerEntryId}"]`);
2573
+ }
2574
+ const wrapper = scopeElement
2575
+ ? scopeElement.querySelector('.expression-editor-wrapper')
2576
+ : document.querySelector('.expression-editor-wrapper');
2577
+ if (wrapper) {
2578
+ wrapper.dataset.invalidArgs = JSON.stringify(error.invalidArguments);
2579
+ }
2580
+ for (const idx of error.invalidArguments) {
2581
+ const paramInput = scopeElement
2582
+ ? scopeElement.querySelector(`.expression-editor-param input[data-param-index="${idx}"]`)
2583
+ : document.querySelector(`.expression-editor-param input[data-param-index="${idx}"]`);
2584
+ if (paramInput) {
2585
+ paramInput.classList.add('validation-error-param');
2586
+ const handler = () => {
2587
+ paramInput.classList.remove('validation-error-param');
2588
+ paramInput.removeEventListener('input', handler);
2589
+ };
2590
+ paramInput.addEventListener('input', handler);
2591
+ this._fieldInputCleanupFns.push(() => {
2592
+ paramInput.removeEventListener('input', handler);
2593
+ });
2594
+ }
2595
+ }
2596
+ }
2597
+ }
2598
+ }, 50);
2599
+ }
1631
2600
  clearValidationErrors() {
1632
- const modeler = this.isReadOnlyProcess$.getValue() ? this._bpmnViewer : this._bpmnModeler;
2601
+ const modeler = this.activeEditor;
1633
2602
  if (!modeler)
1634
2603
  return;
1635
2604
  this.processManagementEditorService.setValidationErrors([]);
@@ -1644,6 +2613,21 @@ class ProcessManagementBuilderComponent {
1644
2613
  eventBus.off('element.out', this._validationOutHandler);
1645
2614
  this._validationOutHandler = null;
1646
2615
  }
2616
+ if (this._selectionChangedHandler) {
2617
+ eventBus.off('selection.changed', this._selectionChangedHandler);
2618
+ this._selectionChangedHandler = null;
2619
+ }
2620
+ this._fieldInputCleanupFns.forEach(fn => fn());
2621
+ this._fieldInputCleanupFns = [];
2622
+ document.querySelectorAll('.validation-error-field').forEach(el => {
2623
+ el.classList.remove('validation-error-field');
2624
+ });
2625
+ document.querySelectorAll('.validation-error-param').forEach(el => {
2626
+ el.classList.remove('validation-error-param');
2627
+ });
2628
+ document.querySelectorAll('.expression-editor-wrapper[data-invalid-args]').forEach(el => {
2629
+ delete el.dataset.invalidArgs;
2630
+ });
1647
2631
  for (const elementId of this._validationErrorElementIds) {
1648
2632
  try {
1649
2633
  canvas.removeMarker(elementId, 'highlight-overlay-error');
@@ -1661,6 +2645,209 @@ class ProcessManagementBuilderComponent {
1661
2645
  // ignore
1662
2646
  }
1663
2647
  }
2648
+ highlightAutofilledElements() {
2649
+ this.clearAutofilledHighlights();
2650
+ if (!this._autofilledElements?.length)
2651
+ return;
2652
+ const modeler = this.activeEditor;
2653
+ if (!modeler)
2654
+ return;
2655
+ const overlays = modeler.get('overlays');
2656
+ for (const element of this._autofilledElements) {
2657
+ try {
2658
+ this._autofilledElementIds.push(element.activityId);
2659
+ const tooltipText = this.getAutofilledTooltip();
2660
+ overlays.add(element.activityId, 'autofilled-indicator', {
2661
+ position: { top: -12, left: -12 },
2662
+ html: this.buildAutofilledOverlayElement(element.activityId, tooltipText),
2663
+ });
2664
+ }
2665
+ catch (e) {
2666
+ // Element may not exist on the canvas
2667
+ }
2668
+ }
2669
+ }
2670
+ clearAutofilledHighlights() {
2671
+ const modeler = this.activeEditor;
2672
+ if (!modeler)
2673
+ return;
2674
+ const overlays = modeler.get('overlays');
2675
+ this._autofilledElementIds = [];
2676
+ try {
2677
+ overlays.remove({ type: 'autofilled-indicator' });
2678
+ }
2679
+ catch (e) {
2680
+ // ignore
2681
+ }
2682
+ }
2683
+ buildAutofilledOverlayElement(elementId, tooltipText) {
2684
+ const container = document.createElement('div');
2685
+ container.className = 'autofilled-indicator-overlay';
2686
+ container.dataset.elementId = elementId;
2687
+ const icon = document.createElement('span');
2688
+ icon.className = 'autofilled-indicator-icon';
2689
+ icon.innerHTML = '!';
2690
+ container.appendChild(icon);
2691
+ const tooltip = document.createElement('span');
2692
+ tooltip.className = 'autofilled-indicator-tooltip';
2693
+ tooltip.textContent = tooltipText;
2694
+ container.appendChild(tooltip);
2695
+ return container;
2696
+ }
2697
+ getAutofilledTooltip() {
2698
+ return this.translateService.instant('processManagement.autofilled.generic');
2699
+ }
2700
+ getElementListeners(element) {
2701
+ const extensionElements = element?.businessObject?.extensionElements;
2702
+ const values = extensionElements?.values || [];
2703
+ const executionListeners = values.filter((v) => v.$type === 'camunda:ExecutionListener');
2704
+ const taskListeners = values.filter((v) => v.$type === 'camunda:TaskListener');
2705
+ return {
2706
+ hasExecutionListener: executionListeners.length > 0,
2707
+ hasTaskListener: taskListeners.length > 0,
2708
+ executionListenerCount: executionListeners.length,
2709
+ taskListenerCount: taskListeners.length,
2710
+ };
2711
+ }
2712
+ toggleMarkerVisibility() {
2713
+ this.$markersVisible.update(v => !v);
2714
+ if (this.$markersVisible()) {
2715
+ this.updateActivityMarkers();
2716
+ }
2717
+ else {
2718
+ this.clearActivityMarkers();
2719
+ }
2720
+ }
2721
+ updateActivityMarkers() {
2722
+ this.clearActivityMarkers();
2723
+ if (!this.$markersVisible())
2724
+ return;
2725
+ const modeler = this.activeEditor;
2726
+ if (!modeler)
2727
+ return;
2728
+ const overlays = modeler.get('overlays');
2729
+ const elementRegistry = modeler.get('elementRegistry');
2730
+ const processLinks = this.processManagementEditorService.processLinksForSelectedDefinition;
2731
+ const processLinkActivityIds = new Set(processLinks.map(pl => pl.activityId));
2732
+ const elementMarkers = new Map();
2733
+ elementRegistry.forEach((element) => {
2734
+ if (!element.id || element.type === 'label')
2735
+ return;
2736
+ const listeners = this.getElementListeners(element);
2737
+ const hasProcessLink = processLinkActivityIds.has(element.id);
2738
+ if (listeners.hasExecutionListener || listeners.hasTaskListener || hasProcessLink) {
2739
+ elementMarkers.set(element.id, {
2740
+ hasExecutionListener: listeners.hasExecutionListener,
2741
+ hasTaskListener: listeners.hasTaskListener,
2742
+ hasProcessLink,
2743
+ executionListenerCount: listeners.executionListenerCount,
2744
+ taskListenerCount: listeners.taskListenerCount,
2745
+ });
2746
+ }
2747
+ });
2748
+ for (const [elementId, info] of elementMarkers) {
2749
+ this._activityMarkerElementIds.push(elementId);
2750
+ const badges = this.buildActivityMarkerBadges(info, elementId);
2751
+ if (badges) {
2752
+ try {
2753
+ const element = elementRegistry.get(elementId);
2754
+ const position = this.calculateMarkerPosition(element);
2755
+ overlays.add(elementId, 'activity-marker', {
2756
+ position,
2757
+ html: badges,
2758
+ });
2759
+ }
2760
+ catch (e) {
2761
+ // Element may not exist on canvas
2762
+ }
2763
+ }
2764
+ }
2765
+ }
2766
+ calculateMarkerPosition(element) {
2767
+ if (element?.waypoints?.length > 1) {
2768
+ const waypoints = element.waypoints;
2769
+ const lastPoint = waypoints[waypoints.length - 1];
2770
+ const secondLastPoint = waypoints[waypoints.length - 2];
2771
+ // Compute bounding box from waypoints (same as diagram-js getBBox)
2772
+ const minX = Math.min(...waypoints.map((wp) => wp.x));
2773
+ const minY = Math.min(...waypoints.map((wp) => wp.y));
2774
+ // Direction of the final segment
2775
+ const dx = lastPoint.x - secondLastPoint.x;
2776
+ const dy = lastPoint.y - secondLastPoint.y;
2777
+ const len = Math.sqrt(dx * dx + dy * dy);
2778
+ // Offset back from arrow tip along the line
2779
+ const backOffset = 30;
2780
+ const backX = len > 0 ? (-dx / len) * backOffset : 0;
2781
+ const backY = len > 0 ? (-dy / len) * backOffset : 0;
2782
+ // Perpendicular offset: above for horizontal lines, right for vertical lines
2783
+ const perpOffset = 20;
2784
+ const isMoreHorizontal = Math.abs(dx) > Math.abs(dy);
2785
+ const perpX = isMoreHorizontal ? 0 : perpOffset;
2786
+ const perpY = isMoreHorizontal ? -perpOffset : 0;
2787
+ return {
2788
+ top: lastPoint.y - minY + backY + perpY - 11,
2789
+ left: lastPoint.x - minX + backX + perpX - 11,
2790
+ };
2791
+ }
2792
+ return { top: -12, right: 12 };
2793
+ }
2794
+ buildActivityMarkerBadges(info, elementId) {
2795
+ const container = document.createElement('div');
2796
+ container.className = 'activity-marker-overlay';
2797
+ container.dataset.elementId = elementId;
2798
+ if (info.hasProcessLink) {
2799
+ const tooltip = this.translateService.instant('processManagement.markers.processLink');
2800
+ container.appendChild(this.buildMarkerBadge('process-link', 'P', tooltip));
2801
+ }
2802
+ if (info.hasExecutionListener) {
2803
+ const countSuffix = info.executionListenerCount > 1 ? ` (${info.executionListenerCount})` : '';
2804
+ const tooltip = this.translateService.instant('processManagement.markers.executionListener') + countSuffix;
2805
+ container.appendChild(this.buildMarkerBadge('execution-listener', 'E', tooltip));
2806
+ }
2807
+ if (info.hasTaskListener) {
2808
+ const countSuffix = info.taskListenerCount > 1 ? ` (${info.taskListenerCount})` : '';
2809
+ const tooltip = this.translateService.instant('processManagement.markers.taskListener') + countSuffix;
2810
+ container.appendChild(this.buildMarkerBadge('task-listener', 'T', tooltip));
2811
+ }
2812
+ if (container.children.length === 0)
2813
+ return null;
2814
+ return container;
2815
+ }
2816
+ buildMarkerBadge(type, label, tooltip) {
2817
+ const badge = document.createElement('div');
2818
+ badge.className = `activity-marker-badge activity-marker-badge--${type}`;
2819
+ const icon = document.createElement('span');
2820
+ icon.className = 'activity-marker-badge__icon';
2821
+ icon.textContent = label;
2822
+ badge.appendChild(icon);
2823
+ const text = document.createElement('span');
2824
+ text.className = 'activity-marker-badge__text';
2825
+ text.textContent = tooltip;
2826
+ badge.appendChild(text);
2827
+ return badge;
2828
+ }
2829
+ clearActivityMarkers() {
2830
+ const modeler = this.activeEditor;
2831
+ if (!modeler)
2832
+ return;
2833
+ const overlays = modeler.get('overlays');
2834
+ this._activityMarkerElementIds = [];
2835
+ try {
2836
+ overlays.remove({ type: 'activity-marker' });
2837
+ }
2838
+ catch (e) {
2839
+ // ignore
2840
+ }
2841
+ }
2842
+ scheduleActivityMarkerUpdate() {
2843
+ if (this._activityMarkerUpdateTimeout) {
2844
+ clearTimeout(this._activityMarkerUpdateTimeout);
2845
+ }
2846
+ this._activityMarkerUpdateTimeout = setTimeout(() => {
2847
+ this.updateActivityMarkers();
2848
+ this._activityMarkerUpdateTimeout = null;
2849
+ }, 300);
2850
+ }
1664
2851
  setSelectedProcessDefinitionToLatest(processDefinitions) {
1665
2852
  const latest = getLatestProcessDefinition(processDefinitions);
1666
2853
  if (!latest)
@@ -1675,11 +2862,16 @@ class ProcessManagementBuilderComponent {
1675
2862
  CamundaPlatformPropertiesProviderModule,
1676
2863
  camundaPlatformBehaviors,
1677
2864
  ValtimoPropertiesProviderModule,
2865
+ ExpressionAutocompleteModule,
1678
2866
  ],
1679
2867
  moddleExtensions: { camunda: CamundaBpmnModdle },
1680
2868
  propertiesPanel: { parent: this.modelerPanelElementRef.nativeElement },
1681
2869
  });
1682
2870
  this._bpmnModeler?.attachTo(this.modelerElementRef.nativeElement);
2871
+ // Initialize expression autocomplete
2872
+ this._expressionAutocomplete = this._bpmnModeler.get('expressionAutocomplete');
2873
+ this._expressionAutocomplete?.setPanelContainer(this.modelerPanelElementRef.nativeElement);
2874
+ this.loadProcessBeansForAutocomplete();
1683
2875
  this._bpmnModeler.on('commandStack.changed', () => {
1684
2876
  this.changesPending$.next(true);
1685
2877
  });
@@ -1695,6 +2887,17 @@ class ProcessManagementBuilderComponent {
1695
2887
  });
1696
2888
  this.processManagementEditorService.setActivityIdBusinessIdMap(idMap);
1697
2889
  this.listenToActivityChangesOnModeler();
2890
+ this.updateActivityMarkers();
2891
+ });
2892
+ }
2893
+ loadProcessBeansForAutocomplete() {
2894
+ this.processBeanService.getProcessBeans().subscribe({
2895
+ next: beans => {
2896
+ this._expressionAutocomplete?.setProcessBeans(beans);
2897
+ },
2898
+ error: err => {
2899
+ this.logger.warn('Failed to load process beans for autocomplete', err);
2900
+ },
1698
2901
  });
1699
2902
  }
1700
2903
  initViewer() {
@@ -1702,7 +2905,10 @@ class ProcessManagementBuilderComponent {
1702
2905
  additionalModules: [
1703
2906
  DisableBpmnWriteModule,
1704
2907
  BpmnPropertiesPanelModule,
2908
+ BpmnPropertiesProviderModule,
2909
+ CamundaPlatformPropertiesProviderModule,
1705
2910
  ValtimoPropertiesProviderModule,
2911
+ ReadOnlyPropertiesPanelModule,
1706
2912
  ],
1707
2913
  moddleExtensions: { camunda: CamundaBpmnModdle },
1708
2914
  propertiesPanel: { parent: this.viewerPanelElementRef.nativeElement },
@@ -1713,6 +2919,7 @@ class ProcessManagementBuilderComponent {
1713
2919
  });
1714
2920
  this._bpmnViewer.on('import.done', () => {
1715
2921
  disableCommands(this._bpmnViewer);
2922
+ this.updateActivityMarkers();
1716
2923
  });
1717
2924
  }
1718
2925
  reload() {
@@ -1723,6 +2930,7 @@ class ProcessManagementBuilderComponent {
1723
2930
  this.processLinkStateService.setModalParams(event?.modalParams);
1724
2931
  this.processLinkStateService.setElementName(event?.modalParams?.element?.name ?? '');
1725
2932
  this.processLinkStateService.selectProcessLink(event.processLink);
2933
+ this.processLinkStateService.setReadOnly(!this.processManagementEditorService.editingAllowed);
1726
2934
  this.processLinkStateService.showModal();
1727
2935
  }
1728
2936
  handleCreateEvent(event) {
@@ -1763,6 +2971,7 @@ class ProcessManagementBuilderComponent {
1763
2971
  this.processManagementEditorService.createProcessLink(event);
1764
2972
  this.processLinkStateService.stopSaving();
1765
2973
  this.processLinkStateService.closeModal();
2974
+ this.updateActivityMarkers();
1766
2975
  const buildingBlockProcessLinkCreateDto = event;
1767
2976
  if (buildingBlockProcessLinkCreateDto.buildingBlockDefinitionKey &&
1768
2977
  buildingBlockProcessLinkCreateDto.buildingBlockDefinitionVersionTag) {
@@ -1775,9 +2984,39 @@ class ProcessManagementBuilderComponent {
1775
2984
  this.processManagementEditorService.deleteProcessLink(event);
1776
2985
  this.processLinkStateService.stopSaving();
1777
2986
  this.processLinkStateService.closeModal();
2987
+ this.updateActivityMarkers();
1778
2988
  this.unsetCalledElementForBuildingBlockProcessLink(event.activityId);
1779
2989
  }));
1780
2990
  }
2991
+ subscribeToViewerActive() {
2992
+ this._subscriptions.add(this.viewerActive$.subscribe(viewerActive => {
2993
+ this._viewerActive = viewerActive;
2994
+ this.updateActivityMarkers();
2995
+ this.highlightAutofilledElements();
2996
+ }));
2997
+ this._subscriptions.add(this.hasEditPermissions$.subscribe(hasEditPermissions => this.processManagementEditorService.setEditingAllowed(hasEditPermissions)));
2998
+ }
2999
+ subscribeToAutofillDismissEvents() {
3000
+ this._subscriptions.add(this.processManagementEditorService.autofillDismissed$.subscribe(activityId => {
3001
+ this.removeAutofillOverlay(activityId);
3002
+ }));
3003
+ }
3004
+ removeAutofillOverlay(activityId) {
3005
+ const modeler = this.activeEditor;
3006
+ if (!modeler)
3007
+ return;
3008
+ const overlays = modeler.get('overlays');
3009
+ try {
3010
+ overlays.remove({ element: activityId, type: 'autofilled-indicator' });
3011
+ }
3012
+ catch (e) {
3013
+ // ignore
3014
+ }
3015
+ const idx = this._autofilledElementIds.indexOf(activityId);
3016
+ if (idx > -1) {
3017
+ this._autofilledElementIds.splice(idx, 1);
3018
+ }
3019
+ }
1781
3020
  initIfCreate() {
1782
3021
  if (this._selectedProcess$.getValue() !== 'create')
1783
3022
  return;
@@ -1809,12 +3048,17 @@ class ProcessManagementBuilderComponent {
1809
3048
  .subscribe(result => {
1810
3049
  const processDefinitionResult = result;
1811
3050
  this.cleanUpListenersOnModeler();
1812
- this._bpmnModeler?.importXML(processDefinitionResult.bpmn20Xml);
1813
- this._bpmnViewer?.importXML(processDefinitionResult.bpmn20Xml);
1814
- this.draft$.next(processDefinitionResult.draft ??
1815
- this.parseDraftFromXml(processDefinitionResult.bpmn20Xml));
3051
+ this._autofilledElements = processDefinitionResult.autofilledElements ?? [];
3052
+ this.processManagementEditorService.setAutofilledElements(this._autofilledElements);
3053
+ this._bpmnModeler?.importXML(processDefinitionResult.bpmn20Xml).then(() => {
3054
+ this.highlightAutofilledElements();
3055
+ });
3056
+ this._bpmnViewer?.importXML(processDefinitionResult.bpmn20Xml).then(() => {
3057
+ this.highlightAutofilledElements();
3058
+ });
1816
3059
  this.canInitializeDocument$.next(!!processDefinitionResult?.processCaseLink?.canInitializeDocument);
1817
3060
  this.startableByUser$.next(!!processDefinitionResult?.processCaseLink?.startableByUser);
3061
+ this.draft$.next(!!processDefinitionResult?.draft);
1818
3062
  this.loading$.next(false);
1819
3063
  }));
1820
3064
  }
@@ -1869,8 +3113,8 @@ class ProcessManagementBuilderComponent {
1869
3113
  .subscribe();
1870
3114
  }
1871
3115
  setCalledElementForBuildingBlockProcessLink(activityId, buildingBlockDefinitionKey, buildingBlockDefinitionVersionTag) {
1872
- const editor = this._bpmnModeler || this._bpmnViewer;
1873
- if (!editor) {
3116
+ const editors = this.loadedEditors;
3117
+ if (!editors.length) {
1874
3118
  return;
1875
3119
  }
1876
3120
  this.processLinkBuildingBlockApiService
@@ -1878,22 +3122,29 @@ class ProcessManagementBuilderComponent {
1878
3122
  .subscribe({
1879
3123
  next: (mainProcessDefinitionKey) => {
1880
3124
  const versionTag = `BB:${buildingBlockDefinitionKey}:${buildingBlockDefinitionVersionTag}`;
1881
- applyBuildingBlockCalledElement(editor, activityId, mainProcessDefinitionKey, versionTag);
3125
+ editors.forEach(editor => applyBuildingBlockCalledElement(editor, activityId, mainProcessDefinitionKey, versionTag));
1882
3126
  },
1883
3127
  });
1884
3128
  }
1885
3129
  unsetCalledElementForBuildingBlockProcessLink(activityId) {
1886
- const editor = this._bpmnModeler || this._bpmnViewer;
1887
- if (!editor) {
3130
+ this.loadedEditors.forEach(editor => clearBuildingBlockCalledElement(editor, activityId));
3131
+ }
3132
+ onValidationErrorClick(elementId) {
3133
+ const modeler = this.activeEditor;
3134
+ const canvas = modeler.get('canvas');
3135
+ const selection = modeler.get('selection');
3136
+ const elementRegistry = modeler.get('elementRegistry');
3137
+ const element = elementRegistry.get(elementId);
3138
+ if (!element)
1888
3139
  return;
1889
- }
1890
- clearBuildingBlockCalledElement(editor, activityId);
3140
+ canvas.scrollToElement(element, { top: 100, bottom: 100, left: 100, right: 100 });
3141
+ selection.select(element);
1891
3142
  }
1892
3143
  getValidationErrorMessage(error) {
1893
3144
  if (error.errorCode) {
1894
- const translationKey = `processManagement.expressionErrors.${error.errorCode}`;
3145
+ const translationKey = `processManagement.validationErrorCodes.${error.errorCode}`;
1895
3146
  const translated = this.translateService.instant(translationKey, {
1896
- expression: error.expression ? `'${error.expression}'` : '',
3147
+ expression: error.expression ?? '',
1897
3148
  });
1898
3149
  if (translated !== translationKey) {
1899
3150
  return translated;
@@ -1915,13 +3166,13 @@ class ProcessManagementBuilderComponent {
1915
3166
  container.appendChild(text);
1916
3167
  return container;
1917
3168
  }
1918
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementBuilderComponent, deps: [{ token: i1$1.BreadcrumbService }, { token: i2$2.IconService }, { token: i3.NGXLogger }, { token: i1$1.ModalService }, { token: i2$1.GlobalNotificationService }, { token: i1$1.PageHeaderService }, { token: i1$1.PageTitleService }, { token: i5.ProcessLinkService }, { token: i5.ProcessLinkStateService }, { token: ProcessManagementEditorService }, { token: ProcessManagementService }, { token: i7.ProcessService }, { token: i8.ActivatedRoute }, { token: i8.Router }, { token: i9.TranslateService }, { token: i10.PluginTranslationService }, { token: i2$1.EditPermissionsService }, { token: i5.ProcessLinkBuildingBlockApiService }], target: i0.ɵɵFactoryTarget.Component }); }
3169
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementBuilderComponent, deps: [{ token: i1$1.BreadcrumbService }, { token: i2$2.IconService }, { token: i3.NGXLogger }, { token: i1$1.ModalService }, { token: i2.GlobalNotificationService }, { token: i1$1.PageHeaderService }, { token: i1$1.PageTitleService }, { token: i5.ProcessLinkService }, { token: i5.ProcessLinkStateService }, { token: ProcessManagementEditorService }, { token: ProcessManagementService }, { token: i7.ProcessService }, { token: i8.ActivatedRoute }, { token: i8.Router }, { token: i9.TranslateService }, { token: i10.PluginTranslationService }, { token: i2.EditPermissionsService }, { token: i5.ProcessLinkBuildingBlockApiService }, { token: ProcessBeanService }], target: i0.ɵɵFactoryTarget.Component }); }
1919
3170
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: ProcessManagementBuilderComponent, isStandalone: true, selector: "valtimo-process-management-builder", providers: [
1920
3171
  ProcessManagementEditorService,
1921
3172
  ProcessLinkStateService,
1922
3173
  ProcessLinkStepService,
1923
3174
  ProcessLinkButtonService,
1924
- ], viewQueries: [{ propertyName: "modelerElementRef", first: true, predicate: ["modeler"], descendants: true }, { propertyName: "modelerPanelElementRef", first: true, predicate: ["modelerPanel"], descendants: true }, { propertyName: "viewerElementRef", first: true, predicate: ["viewer"], descendants: true }, { propertyName: "viewerPanelElementRef", first: true, predicate: ["viewerPanel"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n fitPage\n [disableOverflow]=\"true\"\n *ngIf=\"{\n processDefinitionVersions: processDefinitionVersions$ | async,\n selectedProcessDefinitionXml: selectedProcessDefinitionXml$ | async,\n loading: loading$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n } as obs\"\n data-carbon-theme=\"g10\"\n class=\"bpmn__container\"\n>\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'none' : 'flex'\"\n >\n <div class=\"bpmn__modeler-canvas\" #modeler>\n <div\n class=\"validation-errors-list\"\n *ngIf=\"validationErrors$ | async as validationErrors\"\n [style.display]=\"validationErrors.length > 0 ? '' : 'none'\"\n >\n <details open>\n <summary class=\"validation-errors-list__header\">\n <span>{{\n 'processManagement.validationErrorsCount'\n | translate: {count: validationErrors.length}\n }}</span>\n <svg\n class=\"validation-errors-list__arrow\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8Z\"\n />\n </svg>\n </summary>\n\n <div class=\"validation-errors-list__items\">\n <button\n *ngFor=\"let error of validationErrors\"\n class=\"validation-errors-list__item\"\n [class.warning]=\"error.severity === 'WARNING'\"\n (click)=\"onValidationErrorClick(error.elementId)\"\n >\n <span\n class=\"validation-errors-list__icon\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ error.severity === 'WARNING' ? '!' : '!' }}</span\n >\n\n <span class=\"validation-errors-list__details\">\n <span class=\"validation-errors-list__element\">{{\n error.elementName || error.elementId\n }}</span>\n\n <span\n class=\"validation-errors-list__reason\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ getValidationErrorMessage(error) }}</span\n >\n </span>\n </button>\n </div>\n </details>\n </div>\n </div>\n\n <div class=\"bpmn__modeler-panel\" #modelerPanel></div>\n </div>\n\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'flex' : 'none'\"\n >\n <div class=\"bpmn__modeler-canvas\" #viewer></div>\n\n <div class=\"bpmn__modeler-panel\" #viewerPanel></div>\n </div>\n</div>\n\n<valtimo-process-link-modal></valtimo-process-link-modal>\n\n<valtimo-confirmation-modal\n [showModalSubject$]=\"showWarningConfirmationModal$\"\n [titleTranslationKey]=\"'processManagement.warningConfirmation.title'\"\n [contentTranslationKey]=\"'processManagement.warningConfirmation.content'\"\n [confirmButtonTextTranslationKey]=\"'processManagement.warningConfirmation.confirm'\"\n [cancelButtonTextTranslationKey]=\"'interface.cancel'\"\n (confirmEvent)=\"onWarningConfirmationConfirm()\"\n (cancelEvent)=\"onWarningConfirmationCancel()\"\n></valtimo-confirmation-modal>\n\n<ng-container renderInPageHeader [fullWidth]=\"true\">\n <ng-template>\n <div\n class=\"page-header-actions\"\n *ngIf=\"{\n processDefinitionVersionsListItems: processDefinitionVersionsListItems$ | async,\n loading: loading$ | async,\n changesPending: changesPending$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n isSystemProcess: isSystemProcess$ | async,\n compactMode: compactMode$ | async,\n creatingNewProcess: creatingNewProcess$ | async,\n context: context$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n draft: draft$ | async,\n canInitializeDocument: canInitializeDocument$ | async,\n startableByUser: startableByUser$ | async,\n updatingProcessDefinitionCaseDefinition: updatingProcessDefinitionCaseDefinition$ | async,\n } as actionsObs\"\n >\n <div class=\"page-header-actions__left-container\">\n <cds-dropdown\n *ngIf=\"!actionsObs.creatingNewProcess && actionsObs?.context === 'independent'\"\n class=\"page-header-actions__version-dropdown\"\n (selected)=\"selectedVersionChange($event)\"\n [disabled]=\"\n actionsObs.loading ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 0 ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 1\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n >\n <cds-dropdown-list\n [items]=\"actionsObs.processDefinitionVersionsListItems || []\"\n ></cds-dropdown-list>\n </cds-dropdown>\n\n <div\n class=\"page-header-actions__tags\"\n *ngIf=\"\n actionsObs.isReadOnlyProcess ||\n !actionsObs.hasEditPermissions ||\n actionsObs.isSystemProcess\n \"\n >\n <cds-tag\n *ngIf=\"actionsObs.isReadOnlyProcess || !actionsObs.hasEditPermissions\"\n type=\"blue\"\n >{{ 'processManagement.readOnly' | translate }}</cds-tag\n >\n\n <cds-tag *ngIf=\"actionsObs.isSystemProcess\" type=\"red\">{{\n 'processManagement.systemProcess' | translate\n }}</cds-tag>\n </div>\n\n <div class=\"page-header-actions__toggles\">\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.draftTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.draft' | translate\"\n [offText]=\"'processManagement.draft' | translate\"\n [checked]=\"actionsObs.draft\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n (checkedChange)=\"onProcessToggleChange('draft', $event)\"\n [attr.data-test-id]=\"testIds.draftToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n @if (actionsObs.context === 'case') {\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.processCreatesCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.canInitializeDocument' | translate\"\n [offText]=\"'processManagement.canInitializeDocument' | translate\"\n [checked]=\"actionsObs.canInitializeDocument\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('canInitializeDocument', $event)\"\n [attr.data-test-id]=\"testIds.startsCaseToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.startableWithinCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.startableByUser' | translate\"\n [offText]=\"'processManagement.startableByUser' | translate\"\n [checked]=\"actionsObs.startableByUser\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('startableByUser', $event)\"\n [attr.data-test-id]=\"testIds.startableByUserToggle\"\n ></cds-toggle\n ></cds-tooltip>\n }\n </div>\n </div>\n\n <div class=\"page-header-actions__buttons\">\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option\n [disabled]=\"actionsObs.loading\"\n (selected)=\"export(actionsObs.isReadOnlyProcess)\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n *ngIf=\"actionsObs.context === 'case'\"\n cdsButton=\"tertiary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n (click)=\"navigateBack(null)\"\n >\n {{ 'interface.back' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"return\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [disabled]=\"actionsObs.loading\"\n (click)=\"validateProcessDefinition(actionsObs.isReadOnlyProcess)\"\n >\n {{ 'processManagement.validate' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"list--checked\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"!actionsObs.changesPending\"\n cdsButton=\"primary\"\n (click)=\"\n !actionsObs.creatingNewProcess\n ? deployChanges(actionsObs.isReadOnlyProcess)\n : deployNewProcessDefinition()\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.deployButton\"\n >\n {{ 'processManagement.deploy' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"deploy\" size=\"16\"></svg>\n </button>\n </div>\n </div>\n </ng-template>\n</ng-container>\n", styles: [":host{display:flex;flex-direction:column;gap:24px}.bpmn__container{position:relative;background-color:var(--cds-layer);width:100%}.bpmn__modeler{width:100%;height:100%;flex-direction:row;padding:1px;box-sizing:border-box;outline:1px solid var(--cds-border-subtle);outline-offset:-1px}.bpmn__modeler ::ng-deep .djs-container>svg{outline:none!important}.bpmn__modeler-canvas{width:100%;position:relative}.bpmn__modeler-panel{width:481px;padding-left:1px;box-shadow:inset 1px 0 0 0 var(--cds-border-subtle);box-sizing:border-box}.bpmn__loading{width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:var(--cds-background-selected)}.page-header-actions{display:flex;width:100%;justify-content:space-between;align-items:center;gap:16px}.page-header-actions__left-container{display:flex;gap:16px;align-items:center}.page-header-actions__tags{display:flex;gap:8px;align-items:center}.page-header-actions__version-dropdown{width:150px}.page-header-actions__toggles{display:flex;gap:16px;align-items:center}.page-header-actions__toggles ::ng-deep cds-toggle{display:inline-flex;align-items:center}.page-header-actions__toggles ::ng-deep .cds--toggle__label-text{display:none}.page-header-actions__buttons{display:flex;gap:16px;align-items:center;flex-shrink:0}.valtimo-process-builder__header{display:flex;justify-content:space-between}.valtimo-process-builder__actions>*:not(:last-child){margin-right:8px}.validation-errors-list{position:absolute;top:8px;right:8px;z-index:10;width:320px;max-height:250px;overflow-y:auto;border:1px solid var(--cds-border-subtle);background-color:var(--cds-layer);border-radius:4px;box-shadow:0 2px 6px #00000026}.validation-errors-list details[open]>summary .validation-errors-list__arrow{transform:rotate(90deg)}.validation-errors-list__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;font-size:14px;font-weight:600;color:var(--cds-support-error, #da1e28);cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none}.validation-errors-list__header::-webkit-details-marker{display:none}.validation-errors-list__arrow{display:inline-flex;width:16px;height:16px;fill:currentColor;transition:transform .15s ease;flex-shrink:0}.validation-errors-list__items{display:flex;flex-direction:column;border-top:1px solid var(--cds-border-subtle)}.validation-errors-list__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border:none;border-bottom:1px solid var(--cds-border-subtle);border-left:3px solid var(--cds-support-error, #da1e28);background-color:var(--cds-notification-error-background, #fff1f1);text-align:left;cursor:pointer;font-family:inherit}.validation-errors-list__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}.validation-errors-list__item:last-child{border-bottom:none}.validation-errors-list__item:hover{filter:brightness(.97)}.validation-errors-list__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;margin-top:1px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}.validation-errors-list__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}.validation-errors-list__details{display:flex;flex-direction:column;gap:2px}.validation-errors-list__element{font-size:13px;font-weight:600;color:var(--cds-text-primary)}.validation-errors-list__reason{font-size:13px;color:var(--cds-text-primary)}::ng-deep .highlight-overlay-error:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-error, #da1e28)!important}::ng-deep .highlight-overlay-warning:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-warning, #f1c21b)!important}::ng-deep .djs-overlay-validation-error{overflow:visible!important;pointer-events:all!important}::ng-deep .validation-error-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-error-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-error-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-error, #da1e28);color:var(--cds-support-error, #da1e28);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-error-overlay:hover ::ng-deep .validation-error-overlay__text,::ng-deep .validation-error-overlay--active ::ng-deep .validation-error-overlay__text{display:block}::ng-deep .validation-warning-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-warning-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-warning-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-warning-overlay:hover ::ng-deep .validation-warning-overlay__text,::ng-deep .validation-warning-overlay--active ::ng-deep .validation-warning-overlay__text{display:block}::ng-deep .validation-errors-panel{padding:8px 12px;display:flex;flex-direction:column;gap:8px}::ng-deep .validation-errors-panel__item{display:flex;align-items:flex-start;gap:8px;padding:8px 12px;background-color:var(--cds-notification-error-background, #fff1f1);border-left:3px solid var(--cds-support-error, #da1e28)}::ng-deep .validation-errors-panel__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}::ng-deep .validation-errors-panel__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}::ng-deep .validation-errors-panel__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}::ng-deep .validation-errors-panel__reason{color:var(--cds-text-primary, #161616);font-size:13px;line-height:18px}::ng-deep .process-link-properties-panel{padding:8px 12px 16px;width:100%;display:flex;flex-direction:column;gap:16px}::ng-deep .process-link-properties-panel__buttons,::ng-deep .process-link-properties-panel__header{width:100%;display:flex;gap:16px}::ng-deep .process-link-properties-panel__header{align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--cds-border-subtle-00);padding-bottom:8px;padding-left:8px;padding-right:8px}::ng-deep .process-link-properties-panel__title{color:var(--cds-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px;margin:auto 0}::ng-deep .process-link-properties-panel__title-container{display:flex;flex-direction:column;gap:4px}::ng-deep .process-link-properties-panel__title-container :last-child{color:var(--cds-text-helper);font-style:italic}::ng-deep .process-link-properties-panel .cds--tag{margin:0}::ng-deep .process-link-properties-panel .cds--btn{flex-shrink:1;width:100%;display:flex;align-items:center}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "directive", type: FitPageDirective, selector: "[fitPage]", inputs: ["spaceAdjustment", "fitPageDisabled", "disableOverflow", "bottomMargin"] }, { kind: "ngmodule", type: LoadingModule }, { kind: "directive", type: RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$2.Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: ["id", "label", "hideLabel", "helperText", "placeholder", "displayValue", "clearText", "size", "type", "theme", "disabled", "readonly", "skeleton", "inline", "disableArrowKeys", "invalid", "invalidText", "warn", "warnText", "appendInline", "scrollableContainer", "itemValueKey", "selectionFeedback", "menuButtonLabel", "selectedLabel", "dropUp", "fluid"], outputs: ["selected", "onClose", "close"] }, { kind: "component", type: i2$2.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$2.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$2.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i9.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i2$2.Tag, selector: "cds-tag, ibm-tag", inputs: ["type", "size", "class", "skeleton"] }, { kind: "ngmodule", type: ProcessLinkModule }, { kind: "component", type: i5.ProcessLinkModalComponent, selector: "valtimo-process-link-modal" }, { kind: "component", type: OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact"] }, { kind: "ngmodule", type: ToggleModule }, { kind: "component", type: i2$2.Toggle, selector: "cds-toggle, ibm-toggle", inputs: ["offText", "onText", "label", "size", "hideLabel", "ariaLabel", "skeleton"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: i2$2.Tooltip, selector: "cds-tooltip, ibm-tooltip", inputs: ["id", "enterDelayMs", "leaveDelayMs", "disabled", "description", "templateContext"] }, { kind: "ngmodule", type: ConfirmationModalModule }, { kind: "component", type: i1$1.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }] }); }
3175
+ ], viewQueries: [{ propertyName: "modelerElementRef", first: true, predicate: ["modeler"], descendants: true }, { propertyName: "modelerPanelElementRef", first: true, predicate: ["modelerPanel"], descendants: true }, { propertyName: "viewerElementRef", first: true, predicate: ["viewer"], descendants: true }, { propertyName: "viewerPanelElementRef", first: true, predicate: ["viewerPanel"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n fitPage\n [disableOverflow]=\"true\"\n *ngIf=\"{\n processDefinitionVersions: processDefinitionVersions$ | async,\n selectedProcessDefinitionXml: selectedProcessDefinitionXml$ | async,\n loading: loading$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n } as obs\"\n data-carbon-theme=\"g10\"\n class=\"bpmn__container\"\n>\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'none' : 'flex'\"\n >\n <div class=\"bpmn__modeler-canvas\" #modeler>\n <cds-tooltip\n align=\"top-right\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.markers.toggleTooltip' | translate\"\n class=\"markers-visibility-toggle-wrapper\"\n >\n <button\n type=\"button\"\n class=\"markers-visibility-toggle\"\n (click)=\"toggleMarkerVisibility()\"\n [attr.data-test-id]=\"testIds.markersVisibilityToggle\"\n >\n <svg cdsIcon=\"view\" *ngIf=\"$markersVisible()\" size=\"16\"></svg>\n <svg cdsIcon=\"view--off\" *ngIf=\"!$markersVisible()\" size=\"16\"></svg>\n <span class=\"markers-visibility-toggle__label\">\n {{\n ($markersVisible()\n ? 'processManagement.markers.hide'\n : 'processManagement.markers.show'\n ) | translate\n }}\n </span>\n </button>\n </cds-tooltip>\n\n <div\n class=\"validation-errors-list\"\n *ngIf=\"validationErrors$ | async as validationErrors\"\n [style.display]=\"validationErrors.length > 0 ? '' : 'none'\"\n [attr.data-test-id]=\"testIds.validationErrors\"\n >\n <details open>\n <summary class=\"validation-errors-list__header\">\n <span>{{\n 'processManagement.validationErrorsCount'\n | translate: {count: validationErrors.length}\n }}</span>\n <svg\n class=\"validation-errors-list__arrow\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8Z\"\n />\n </svg>\n </summary>\n\n <div class=\"validation-errors-list__items\">\n <button\n *ngFor=\"let error of validationErrors\"\n class=\"validation-errors-list__item\"\n [class.warning]=\"error.severity === 'WARNING'\"\n (click)=\"onValidationErrorClick(error.elementId)\"\n >\n <span\n class=\"validation-errors-list__icon\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ error.severity === 'WARNING' ? '!' : '!' }}</span\n >\n\n <span class=\"validation-errors-list__details\">\n <span class=\"validation-errors-list__element\">{{\n error.elementName || error.elementId\n }}</span>\n\n <span\n class=\"validation-errors-list__reason\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ getValidationErrorMessage(error) }}</span\n >\n </span>\n </button>\n </div>\n </details>\n </div>\n </div>\n\n <div class=\"bpmn__modeler-panel\" #modelerPanel></div>\n </div>\n\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'flex' : 'none'\"\n >\n <div class=\"bpmn__modeler-canvas\" #viewer>\n <cds-tooltip\n align=\"top-right\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.markers.toggleTooltip' | translate\"\n class=\"markers-visibility-toggle-wrapper\"\n >\n <button\n type=\"button\"\n class=\"markers-visibility-toggle\"\n (click)=\"toggleMarkerVisibility()\"\n [attr.data-test-id]=\"testIds.markersVisibilityToggle\"\n >\n <svg cdsIcon=\"view\" *ngIf=\"$markersVisible()\" size=\"16\"></svg>\n <svg cdsIcon=\"view--off\" *ngIf=\"!$markersVisible()\" size=\"16\"></svg>\n <span class=\"markers-visibility-toggle__label\">\n {{\n ($markersVisible()\n ? 'processManagement.markers.hide'\n : 'processManagement.markers.show'\n ) | translate\n }}\n </span>\n </button>\n </cds-tooltip>\n </div>\n\n <div class=\"bpmn__modeler-panel bpmn__modeler-panel--read-only\" #viewerPanel></div>\n </div>\n</div>\n\n<valtimo-process-link-modal></valtimo-process-link-modal>\n\n<valtimo-confirmation-modal\n [showModalSubject$]=\"showWarningConfirmationModal$\"\n [titleTranslationKey]=\"'processManagement.warningConfirmation.title'\"\n [contentTranslationKey]=\"'processManagement.warningConfirmation.content'\"\n [confirmButtonTextTranslationKey]=\"'processManagement.warningConfirmation.confirm'\"\n [cancelButtonTextTranslationKey]=\"'interface.cancel'\"\n (confirmEvent)=\"onWarningConfirmationConfirm()\"\n (cancelEvent)=\"onWarningConfirmationCancel()\"\n></valtimo-confirmation-modal>\n\n<ng-container renderInPageHeader [fullWidth]=\"true\">\n <ng-template>\n <div\n class=\"page-header-actions\"\n *ngIf=\"{\n processDefinitionVersionsListItems: processDefinitionVersionsListItems$ | async,\n loading: loading$ | async,\n changesPending: changesPending$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n isSystemProcess: isSystemProcess$ | async,\n compactMode: compactMode$ | async,\n creatingNewProcess: creatingNewProcess$ | async,\n context: context$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n draft: draft$ | async,\n canInitializeDocument: canInitializeDocument$ | async,\n startableByUser: startableByUser$ | async,\n updatingProcessDefinitionCaseDefinition: updatingProcessDefinitionCaseDefinition$ | async,\n } as actionsObs\"\n >\n <div class=\"page-header-actions__left-container\">\n <cds-dropdown\n *ngIf=\"!actionsObs.creatingNewProcess && actionsObs?.context === 'independent'\"\n class=\"page-header-actions__version-dropdown\"\n [attr.data-test-id]=\"testIds.versionDropdown\"\n (selected)=\"selectedVersionChange($event)\"\n [disabled]=\"\n actionsObs.loading ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 0 ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 1\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n >\n <cds-dropdown-list\n [items]=\"actionsObs.processDefinitionVersionsListItems || []\"\n ></cds-dropdown-list>\n </cds-dropdown>\n\n <div\n class=\"page-header-actions__tags\"\n *ngIf=\"\n actionsObs.isReadOnlyProcess ||\n !actionsObs.hasEditPermissions ||\n actionsObs.isSystemProcess\n \"\n >\n <cds-tag\n *ngIf=\"actionsObs.isReadOnlyProcess || !actionsObs.hasEditPermissions\"\n type=\"blue\"\n [attr.data-test-id]=\"testIds.readOnlyTag\"\n >{{ 'processManagement.readOnly' | translate }}</cds-tag\n >\n\n <cds-tag\n *ngIf=\"actionsObs.isSystemProcess\"\n type=\"red\"\n [attr.data-test-id]=\"testIds.systemProcessTag\"\n >{{ 'processManagement.systemProcess' | translate }}</cds-tag\n >\n </div>\n\n <div class=\"page-header-actions__toggles\">\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.draftTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.draft' | translate\"\n [offText]=\"'processManagement.draft' | translate\"\n [checked]=\"actionsObs.draft\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n (checkedChange)=\"onProcessToggleChange('draft', $event)\"\n [attr.data-test-id]=\"testIds.draftToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n @if (actionsObs.context === 'case') {\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.processCreatesCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.canInitializeDocument' | translate\"\n [offText]=\"'processManagement.canInitializeDocument' | translate\"\n [checked]=\"actionsObs.canInitializeDocument\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('canInitializeDocument', $event)\"\n [attr.data-test-id]=\"testIds.startsCaseToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.startableWithinCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.startableByUser' | translate\"\n [offText]=\"'processManagement.startableByUser' | translate\"\n [checked]=\"actionsObs.startableByUser\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('startableByUser', $event)\"\n [attr.data-test-id]=\"testIds.startableByUserToggle\"\n ></cds-toggle\n ></cds-tooltip>\n }\n </div>\n </div>\n\n <div class=\"page-header-actions__buttons\">\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger\n overflowTrigger\n [attr.data-test-id]=\"testIds.moreButton\"\n ></v-overflow-menu-trigger>\n <v-overflow-menu-option\n *ngIf=\"actionsObs.context !== 'case'\"\n [disabled]=\"actionsObs.loading\"\n [attr.data-test-id]=\"testIds.exportOption\"\n (selected)=\"export()\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"actionsObs.loading\"\n (selected)=\"exportBpmn(actionsObs.isReadOnlyProcess)\"\n >{{ 'processManagement.exportBpmn' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n *ngIf=\"actionsObs.context === 'case'\"\n cdsButton=\"tertiary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n (click)=\"navigateBack(null)\"\n >\n {{ 'interface.back' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"return\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n [attr.data-test-id]=\"testIds.validateButton\"\n (click)=\"validateProcessDefinition(actionsObs.isReadOnlyProcess)\"\n >\n {{ 'processManagement.validate' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"list--checked\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"!actionsObs.changesPending || !actionsObs.hasEditPermissions\"\n cdsButton=\"primary\"\n (click)=\"\n !actionsObs.creatingNewProcess\n ? deployChanges(actionsObs.isReadOnlyProcess)\n : deployNewProcessDefinition()\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.deployButton\"\n >\n {{ 'processManagement.deploy' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"deploy\" size=\"16\"></svg>\n </button>\n </div>\n </div>\n </ng-template>\n</ng-container>\n", styles: [":host{display:flex;flex-direction:column;gap:24px}.bpmn__container{position:relative;background-color:var(--cds-layer);width:100%}.bpmn__modeler{width:100%;height:100%;flex-direction:row;padding:1px;box-sizing:border-box;outline:1px solid var(--cds-border-subtle);outline-offset:-1px}.bpmn__modeler ::ng-deep .djs-container>svg{outline:none!important}.bpmn__modeler-canvas{width:100%;position:relative}.bpmn__modeler-canvas .markers-visibility-toggle-wrapper{position:absolute;bottom:55px;right:15px;z-index:100}.bpmn__modeler-canvas .markers-visibility-toggle{display:flex;align-items:center;gap:6px;height:32px;padding:0 10px;border:none;border-radius:2px;background-color:var(--cds-layer);cursor:pointer;color:var(--cds-text-secondary);transition:background-color .15s,color .15s}.bpmn__modeler-canvas .markers-visibility-toggle:hover{background-color:var(--cds-layer-hover);color:var(--cds-text-primary)}.bpmn__modeler-canvas .markers-visibility-toggle:focus{outline:2px solid var(--cds-focus);outline-offset:-2px}.bpmn__modeler-canvas .markers-visibility-toggle__label{font-size:12px;white-space:nowrap}.bpmn__modeler-panel{width:481px;padding-left:1px;box-shadow:inset 1px 0 0 0 var(--cds-border-subtle);box-sizing:border-box}.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry input,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry select,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry textarea,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry .bio-properties-panel-feel-editor-container{opacity:.65}.bpmn__loading{width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:var(--cds-background-selected)}.page-header-actions{display:flex;width:100%;justify-content:space-between;align-items:center;gap:16px}.page-header-actions__left-container{display:flex;gap:16px;align-items:center}.page-header-actions__tags{display:flex;gap:8px;align-items:center}.page-header-actions__version-dropdown{width:150px}.page-header-actions__toggles{display:flex;gap:16px;align-items:center}.page-header-actions__toggles ::ng-deep cds-toggle{display:inline-flex;align-items:center}.page-header-actions__toggles ::ng-deep .cds--toggle__label-text{display:none}.page-header-actions__buttons{display:flex;gap:16px;align-items:center;flex-shrink:0}.valtimo-process-builder__header{display:flex;justify-content:space-between}.valtimo-process-builder__actions>*:not(:last-child){margin-right:8px}.validation-errors-list{position:absolute;top:8px;right:8px;z-index:10;width:320px;max-height:250px;overflow-y:auto;border:1px solid var(--cds-border-subtle);background-color:var(--cds-layer);border-radius:4px;box-shadow:0 2px 6px #00000026}.validation-errors-list details[open]>summary .validation-errors-list__arrow{transform:rotate(90deg)}.validation-errors-list__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;font-size:14px;font-weight:600;color:var(--cds-support-error, #da1e28);cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none}.validation-errors-list__header::-webkit-details-marker{display:none}.validation-errors-list__arrow{display:inline-flex;width:16px;height:16px;fill:currentColor;transition:transform .15s ease;flex-shrink:0}.validation-errors-list__items{display:flex;flex-direction:column;border-top:1px solid var(--cds-border-subtle)}.validation-errors-list__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border:none;border-bottom:1px solid var(--cds-border-subtle);border-left:3px solid var(--cds-support-error, #da1e28);background-color:var(--cds-notification-error-background, #fff1f1);text-align:left;cursor:pointer;font-family:inherit}.validation-errors-list__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}.validation-errors-list__item:last-child{border-bottom:none}.validation-errors-list__item:hover{filter:brightness(.97)}.validation-errors-list__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;margin-top:1px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}.validation-errors-list__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}.validation-errors-list__details{display:flex;flex-direction:column;gap:2px}.validation-errors-list__element{font-size:13px;font-weight:600;color:var(--cds-text-primary)}.validation-errors-list__reason{font-size:13px;color:var(--cds-text-primary)}::ng-deep .highlight-overlay-error:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-error, #da1e28)!important}::ng-deep .highlight-overlay-warning:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-warning, #f1c21b)!important}::ng-deep .djs-overlay-validation-error{overflow:visible!important;pointer-events:all!important}::ng-deep .validation-error-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-error-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-error-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-error, #da1e28);color:var(--cds-support-error, #da1e28);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-error-overlay:hover ::ng-deep .validation-error-overlay__text,::ng-deep .validation-error-overlay--active ::ng-deep .validation-error-overlay__text{display:block}::ng-deep .validation-warning-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-warning-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-warning-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-warning-overlay:hover ::ng-deep .validation-warning-overlay__text,::ng-deep .validation-warning-overlay--active ::ng-deep .validation-warning-overlay__text{display:block}::ng-deep .djs-overlay-activity-marker{overflow:visible!important;pointer-events:all!important}::ng-deep .activity-marker-overlay{display:flex;gap:2px}::ng-deep .activity-marker-badge{position:relative;cursor:pointer;pointer-events:all}::ng-deep .activity-marker-badge__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:14px;font-weight:700;line-height:0}::ng-deep .activity-marker-badge__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .activity-marker-badge:hover ::ng-deep .activity-marker-badge__text,::ng-deep .activity-marker-badge--active ::ng-deep .activity-marker-badge__text{display:block}::ng-deep .activity-marker-badge--process-link ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--process-link ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-support-info, #0043ce);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .activity-marker-badge--execution-listener ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-purple-60, #8a3ffc);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--execution-listener ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-purple-60, #8a3ffc);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .activity-marker-badge--task-listener ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-teal-50, #009d9a);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--task-listener ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-teal-50, #009d9a);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .validation-errors-panel{padding:8px 12px;display:flex;flex-direction:column;gap:8px}::ng-deep .validation-errors-panel__item{display:flex;align-items:flex-start;gap:8px;padding:8px 12px;background-color:var(--cds-notification-error-background, #fff1f1);border-left:3px solid var(--cds-support-error, #da1e28)}::ng-deep .validation-errors-panel__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}::ng-deep .validation-errors-panel__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}::ng-deep .validation-errors-panel__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}::ng-deep .validation-errors-panel__reason{color:var(--cds-text-primary, #161616);font-size:13px;line-height:18px}::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-input,::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-textfield,::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-feel-editor-container{border:2px solid var(--cds-support-error, #da1e28)!important;background-color:var(--cds-notification-error-background, #fff1f1)!important}::ng-deep .expression-editor-param input.validation-error-param,::ng-deep input.expression-editor-input.validation-error-param,::ng-deep .expression-editor-technical input.validation-error-param,::ng-deep .expression-editor-technical textarea.validation-error-param{border:2px solid #da1e28!important;background-color:#fff1f1!important;border-color:#da1e28!important}::ng-deep .process-link-properties-panel{padding:8px 12px 16px;width:100%;display:flex;flex-direction:column;gap:16px}::ng-deep .process-link-properties-panel__buttons,::ng-deep .process-link-properties-panel__header{width:100%;display:flex;gap:16px}::ng-deep .process-link-properties-panel__header{align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--cds-border-subtle-00);padding-bottom:8px;padding-left:8px;padding-right:8px}::ng-deep .process-link-properties-panel__title{color:var(--cds-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px;margin:auto 0}::ng-deep .process-link-properties-panel__title-container{display:flex;flex-direction:column;gap:4px}::ng-deep .process-link-properties-panel__title-container :last-child{color:var(--cds-text-helper);font-style:italic}::ng-deep .process-link-properties-panel .cds--tag{margin:0}::ng-deep .process-link-properties-panel .cds--btn{flex-shrink:1;width:100%;display:flex;align-items:center}::ng-deep .djs-overlay-autofilled-indicator{overflow:visible!important;pointer-events:all!important}::ng-deep .autofilled-indicator-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .autofilled-indicator-overlay .autofilled-indicator-icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .autofilled-indicator-overlay .autofilled-indicator-tooltip{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-info, #0043ce);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .autofilled-indicator-overlay:hover .autofilled-indicator-tooltip{display:block}::ng-deep .autofilled-notification-panel{display:flex;align-items:center;gap:8px;padding:8px 12px;background-color:var(--cds-layer-02, #f4f4f4);border-left:3px solid var(--cds-support-info, #0043ce);margin-bottom:8px}::ng-deep .autofilled-notification-panel__icon{display:flex;align-items:center;justify-content:center;min-width:18px;height:18px;border-radius:50%;background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700}::ng-deep .autofilled-notification-panel__message{flex:1;font-size:12px;color:var(--cds-text-primary, #161616)}::ng-deep .autofilled-notification-panel__dismiss{background:none;border:none;cursor:pointer;padding:2px 6px;font-size:16px;color:var(--cds-text-secondary, #525252);line-height:1}::ng-deep .autofilled-notification-panel__dismiss:hover{color:var(--cds-text-primary, #161616)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "directive", type: FitPageDirective, selector: "[fitPage]", inputs: ["spaceAdjustment", "fitPageDisabled", "disableOverflow", "bottomMargin"] }, { kind: "ngmodule", type: LoadingModule }, { kind: "directive", type: RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$2.Dropdown, selector: "cds-dropdown, ibm-dropdown", inputs: ["id", "label", "hideLabel", "helperText", "placeholder", "displayValue", "clearText", "size", "type", "theme", "disabled", "readonly", "skeleton", "inline", "disableArrowKeys", "invalid", "invalidText", "warn", "warnText", "appendInline", "scrollableContainer", "itemValueKey", "selectionFeedback", "menuButtonLabel", "selectedLabel", "dropUp", "fluid"], outputs: ["selected", "onClose", "close"] }, { kind: "component", type: i2$2.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$2.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$2.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i9.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i2$2.Tag, selector: "cds-tag, ibm-tag", inputs: ["type", "size", "class", "skeleton"] }, { kind: "ngmodule", type: ProcessLinkModule }, { kind: "component", type: i5.ProcessLinkModalComponent, selector: "valtimo-process-link-modal" }, { kind: "ngmodule", type: ToggleModule }, { kind: "component", type: i2$2.Toggle, selector: "cds-toggle, ibm-toggle", inputs: ["offText", "onText", "label", "size", "hideLabel", "ariaLabel", "skeleton"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: i2$2.Tooltip, selector: "cds-tooltip, ibm-tooltip", inputs: ["id", "enterDelayMs", "leaveDelayMs", "disabled", "description", "templateContext"] }, { kind: "ngmodule", type: ConfirmationModalModule }, { kind: "component", type: i1$1.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "ngmodule", type: OverflowMenuModule }, { kind: "component", type: i1$1.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i1$1.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i1$1.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact", "label"] }] }); }
1925
3176
  }
1926
3177
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementBuilderComponent, decorators: [{
1927
3178
  type: Component,
@@ -1938,20 +3189,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
1938
3189
  TranslateModule,
1939
3190
  TagModule,
1940
3191
  ProcessLinkModule,
1941
- ProcessLinkModule,
1942
- OverflowMenuComponent,
1943
- OverflowMenuOptionComponent,
1944
- OverflowMenuTriggerComponent,
1945
3192
  ToggleModule,
1946
3193
  TooltipModule,
1947
3194
  ConfirmationModalModule,
3195
+ OverflowMenuModule,
1948
3196
  ], providers: [
1949
3197
  ProcessManagementEditorService,
1950
3198
  ProcessLinkStateService,
1951
3199
  ProcessLinkStepService,
1952
3200
  ProcessLinkButtonService,
1953
- ], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n fitPage\n [disableOverflow]=\"true\"\n *ngIf=\"{\n processDefinitionVersions: processDefinitionVersions$ | async,\n selectedProcessDefinitionXml: selectedProcessDefinitionXml$ | async,\n loading: loading$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n } as obs\"\n data-carbon-theme=\"g10\"\n class=\"bpmn__container\"\n>\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'none' : 'flex'\"\n >\n <div class=\"bpmn__modeler-canvas\" #modeler>\n <div\n class=\"validation-errors-list\"\n *ngIf=\"validationErrors$ | async as validationErrors\"\n [style.display]=\"validationErrors.length > 0 ? '' : 'none'\"\n >\n <details open>\n <summary class=\"validation-errors-list__header\">\n <span>{{\n 'processManagement.validationErrorsCount'\n | translate: {count: validationErrors.length}\n }}</span>\n <svg\n class=\"validation-errors-list__arrow\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8Z\"\n />\n </svg>\n </summary>\n\n <div class=\"validation-errors-list__items\">\n <button\n *ngFor=\"let error of validationErrors\"\n class=\"validation-errors-list__item\"\n [class.warning]=\"error.severity === 'WARNING'\"\n (click)=\"onValidationErrorClick(error.elementId)\"\n >\n <span\n class=\"validation-errors-list__icon\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ error.severity === 'WARNING' ? '!' : '!' }}</span\n >\n\n <span class=\"validation-errors-list__details\">\n <span class=\"validation-errors-list__element\">{{\n error.elementName || error.elementId\n }}</span>\n\n <span\n class=\"validation-errors-list__reason\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ getValidationErrorMessage(error) }}</span\n >\n </span>\n </button>\n </div>\n </details>\n </div>\n </div>\n\n <div class=\"bpmn__modeler-panel\" #modelerPanel></div>\n </div>\n\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'flex' : 'none'\"\n >\n <div class=\"bpmn__modeler-canvas\" #viewer></div>\n\n <div class=\"bpmn__modeler-panel\" #viewerPanel></div>\n </div>\n</div>\n\n<valtimo-process-link-modal></valtimo-process-link-modal>\n\n<valtimo-confirmation-modal\n [showModalSubject$]=\"showWarningConfirmationModal$\"\n [titleTranslationKey]=\"'processManagement.warningConfirmation.title'\"\n [contentTranslationKey]=\"'processManagement.warningConfirmation.content'\"\n [confirmButtonTextTranslationKey]=\"'processManagement.warningConfirmation.confirm'\"\n [cancelButtonTextTranslationKey]=\"'interface.cancel'\"\n (confirmEvent)=\"onWarningConfirmationConfirm()\"\n (cancelEvent)=\"onWarningConfirmationCancel()\"\n></valtimo-confirmation-modal>\n\n<ng-container renderInPageHeader [fullWidth]=\"true\">\n <ng-template>\n <div\n class=\"page-header-actions\"\n *ngIf=\"{\n processDefinitionVersionsListItems: processDefinitionVersionsListItems$ | async,\n loading: loading$ | async,\n changesPending: changesPending$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n isSystemProcess: isSystemProcess$ | async,\n compactMode: compactMode$ | async,\n creatingNewProcess: creatingNewProcess$ | async,\n context: context$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n draft: draft$ | async,\n canInitializeDocument: canInitializeDocument$ | async,\n startableByUser: startableByUser$ | async,\n updatingProcessDefinitionCaseDefinition: updatingProcessDefinitionCaseDefinition$ | async,\n } as actionsObs\"\n >\n <div class=\"page-header-actions__left-container\">\n <cds-dropdown\n *ngIf=\"!actionsObs.creatingNewProcess && actionsObs?.context === 'independent'\"\n class=\"page-header-actions__version-dropdown\"\n (selected)=\"selectedVersionChange($event)\"\n [disabled]=\"\n actionsObs.loading ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 0 ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 1\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n >\n <cds-dropdown-list\n [items]=\"actionsObs.processDefinitionVersionsListItems || []\"\n ></cds-dropdown-list>\n </cds-dropdown>\n\n <div\n class=\"page-header-actions__tags\"\n *ngIf=\"\n actionsObs.isReadOnlyProcess ||\n !actionsObs.hasEditPermissions ||\n actionsObs.isSystemProcess\n \"\n >\n <cds-tag\n *ngIf=\"actionsObs.isReadOnlyProcess || !actionsObs.hasEditPermissions\"\n type=\"blue\"\n >{{ 'processManagement.readOnly' | translate }}</cds-tag\n >\n\n <cds-tag *ngIf=\"actionsObs.isSystemProcess\" type=\"red\">{{\n 'processManagement.systemProcess' | translate\n }}</cds-tag>\n </div>\n\n <div class=\"page-header-actions__toggles\">\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.draftTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.draft' | translate\"\n [offText]=\"'processManagement.draft' | translate\"\n [checked]=\"actionsObs.draft\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n (checkedChange)=\"onProcessToggleChange('draft', $event)\"\n [attr.data-test-id]=\"testIds.draftToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n @if (actionsObs.context === 'case') {\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.processCreatesCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.canInitializeDocument' | translate\"\n [offText]=\"'processManagement.canInitializeDocument' | translate\"\n [checked]=\"actionsObs.canInitializeDocument\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('canInitializeDocument', $event)\"\n [attr.data-test-id]=\"testIds.startsCaseToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.startableWithinCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.startableByUser' | translate\"\n [offText]=\"'processManagement.startableByUser' | translate\"\n [checked]=\"actionsObs.startableByUser\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('startableByUser', $event)\"\n [attr.data-test-id]=\"testIds.startableByUserToggle\"\n ></cds-toggle\n ></cds-tooltip>\n }\n </div>\n </div>\n\n <div class=\"page-header-actions__buttons\">\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger overflowTrigger></v-overflow-menu-trigger>\n <v-overflow-menu-option\n [disabled]=\"actionsObs.loading\"\n (selected)=\"export(actionsObs.isReadOnlyProcess)\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n *ngIf=\"actionsObs.context === 'case'\"\n cdsButton=\"tertiary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n (click)=\"navigateBack(null)\"\n >\n {{ 'interface.back' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"return\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [disabled]=\"actionsObs.loading\"\n (click)=\"validateProcessDefinition(actionsObs.isReadOnlyProcess)\"\n >\n {{ 'processManagement.validate' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"list--checked\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"!actionsObs.changesPending\"\n cdsButton=\"primary\"\n (click)=\"\n !actionsObs.creatingNewProcess\n ? deployChanges(actionsObs.isReadOnlyProcess)\n : deployNewProcessDefinition()\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.deployButton\"\n >\n {{ 'processManagement.deploy' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"deploy\" size=\"16\"></svg>\n </button>\n </div>\n </div>\n </ng-template>\n</ng-container>\n", styles: [":host{display:flex;flex-direction:column;gap:24px}.bpmn__container{position:relative;background-color:var(--cds-layer);width:100%}.bpmn__modeler{width:100%;height:100%;flex-direction:row;padding:1px;box-sizing:border-box;outline:1px solid var(--cds-border-subtle);outline-offset:-1px}.bpmn__modeler ::ng-deep .djs-container>svg{outline:none!important}.bpmn__modeler-canvas{width:100%;position:relative}.bpmn__modeler-panel{width:481px;padding-left:1px;box-shadow:inset 1px 0 0 0 var(--cds-border-subtle);box-sizing:border-box}.bpmn__loading{width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:var(--cds-background-selected)}.page-header-actions{display:flex;width:100%;justify-content:space-between;align-items:center;gap:16px}.page-header-actions__left-container{display:flex;gap:16px;align-items:center}.page-header-actions__tags{display:flex;gap:8px;align-items:center}.page-header-actions__version-dropdown{width:150px}.page-header-actions__toggles{display:flex;gap:16px;align-items:center}.page-header-actions__toggles ::ng-deep cds-toggle{display:inline-flex;align-items:center}.page-header-actions__toggles ::ng-deep .cds--toggle__label-text{display:none}.page-header-actions__buttons{display:flex;gap:16px;align-items:center;flex-shrink:0}.valtimo-process-builder__header{display:flex;justify-content:space-between}.valtimo-process-builder__actions>*:not(:last-child){margin-right:8px}.validation-errors-list{position:absolute;top:8px;right:8px;z-index:10;width:320px;max-height:250px;overflow-y:auto;border:1px solid var(--cds-border-subtle);background-color:var(--cds-layer);border-radius:4px;box-shadow:0 2px 6px #00000026}.validation-errors-list details[open]>summary .validation-errors-list__arrow{transform:rotate(90deg)}.validation-errors-list__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;font-size:14px;font-weight:600;color:var(--cds-support-error, #da1e28);cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none}.validation-errors-list__header::-webkit-details-marker{display:none}.validation-errors-list__arrow{display:inline-flex;width:16px;height:16px;fill:currentColor;transition:transform .15s ease;flex-shrink:0}.validation-errors-list__items{display:flex;flex-direction:column;border-top:1px solid var(--cds-border-subtle)}.validation-errors-list__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border:none;border-bottom:1px solid var(--cds-border-subtle);border-left:3px solid var(--cds-support-error, #da1e28);background-color:var(--cds-notification-error-background, #fff1f1);text-align:left;cursor:pointer;font-family:inherit}.validation-errors-list__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}.validation-errors-list__item:last-child{border-bottom:none}.validation-errors-list__item:hover{filter:brightness(.97)}.validation-errors-list__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;margin-top:1px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}.validation-errors-list__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}.validation-errors-list__details{display:flex;flex-direction:column;gap:2px}.validation-errors-list__element{font-size:13px;font-weight:600;color:var(--cds-text-primary)}.validation-errors-list__reason{font-size:13px;color:var(--cds-text-primary)}::ng-deep .highlight-overlay-error:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-error, #da1e28)!important}::ng-deep .highlight-overlay-warning:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-warning, #f1c21b)!important}::ng-deep .djs-overlay-validation-error{overflow:visible!important;pointer-events:all!important}::ng-deep .validation-error-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-error-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-error-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-error, #da1e28);color:var(--cds-support-error, #da1e28);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-error-overlay:hover ::ng-deep .validation-error-overlay__text,::ng-deep .validation-error-overlay--active ::ng-deep .validation-error-overlay__text{display:block}::ng-deep .validation-warning-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-warning-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-warning-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-warning-overlay:hover ::ng-deep .validation-warning-overlay__text,::ng-deep .validation-warning-overlay--active ::ng-deep .validation-warning-overlay__text{display:block}::ng-deep .validation-errors-panel{padding:8px 12px;display:flex;flex-direction:column;gap:8px}::ng-deep .validation-errors-panel__item{display:flex;align-items:flex-start;gap:8px;padding:8px 12px;background-color:var(--cds-notification-error-background, #fff1f1);border-left:3px solid var(--cds-support-error, #da1e28)}::ng-deep .validation-errors-panel__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}::ng-deep .validation-errors-panel__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}::ng-deep .validation-errors-panel__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}::ng-deep .validation-errors-panel__reason{color:var(--cds-text-primary, #161616);font-size:13px;line-height:18px}::ng-deep .process-link-properties-panel{padding:8px 12px 16px;width:100%;display:flex;flex-direction:column;gap:16px}::ng-deep .process-link-properties-panel__buttons,::ng-deep .process-link-properties-panel__header{width:100%;display:flex;gap:16px}::ng-deep .process-link-properties-panel__header{align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--cds-border-subtle-00);padding-bottom:8px;padding-left:8px;padding-right:8px}::ng-deep .process-link-properties-panel__title{color:var(--cds-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px;margin:auto 0}::ng-deep .process-link-properties-panel__title-container{display:flex;flex-direction:column;gap:4px}::ng-deep .process-link-properties-panel__title-container :last-child{color:var(--cds-text-helper);font-style:italic}::ng-deep .process-link-properties-panel .cds--tag{margin:0}::ng-deep .process-link-properties-panel .cds--btn{flex-shrink:1;width:100%;display:flex;align-items:center}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
1954
- }], ctorParameters: () => [{ type: i1$1.BreadcrumbService }, { type: i2$2.IconService }, { type: i3.NGXLogger }, { type: i1$1.ModalService }, { type: i2$1.GlobalNotificationService }, { type: i1$1.PageHeaderService }, { type: i1$1.PageTitleService }, { type: i5.ProcessLinkService }, { type: i5.ProcessLinkStateService }, { type: ProcessManagementEditorService }, { type: ProcessManagementService }, { type: i7.ProcessService }, { type: i8.ActivatedRoute }, { type: i8.Router }, { type: i9.TranslateService }, { type: i10.PluginTranslationService }, { type: i2$1.EditPermissionsService }, { type: i5.ProcessLinkBuildingBlockApiService }], propDecorators: { modelerElementRef: [{
3201
+ ], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<div\n fitPage\n [disableOverflow]=\"true\"\n *ngIf=\"{\n processDefinitionVersions: processDefinitionVersions$ | async,\n selectedProcessDefinitionXml: selectedProcessDefinitionXml$ | async,\n loading: loading$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n } as obs\"\n data-carbon-theme=\"g10\"\n class=\"bpmn__container\"\n>\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'none' : 'flex'\"\n >\n <div class=\"bpmn__modeler-canvas\" #modeler>\n <cds-tooltip\n align=\"top-right\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.markers.toggleTooltip' | translate\"\n class=\"markers-visibility-toggle-wrapper\"\n >\n <button\n type=\"button\"\n class=\"markers-visibility-toggle\"\n (click)=\"toggleMarkerVisibility()\"\n [attr.data-test-id]=\"testIds.markersVisibilityToggle\"\n >\n <svg cdsIcon=\"view\" *ngIf=\"$markersVisible()\" size=\"16\"></svg>\n <svg cdsIcon=\"view--off\" *ngIf=\"!$markersVisible()\" size=\"16\"></svg>\n <span class=\"markers-visibility-toggle__label\">\n {{\n ($markersVisible()\n ? 'processManagement.markers.hide'\n : 'processManagement.markers.show'\n ) | translate\n }}\n </span>\n </button>\n </cds-tooltip>\n\n <div\n class=\"validation-errors-list\"\n *ngIf=\"validationErrors$ | async as validationErrors\"\n [style.display]=\"validationErrors.length > 0 ? '' : 'none'\"\n [attr.data-test-id]=\"testIds.validationErrors\"\n >\n <details open>\n <summary class=\"validation-errors-list__header\">\n <span>{{\n 'processManagement.validationErrorsCount'\n | translate: {count: validationErrors.length}\n }}</span>\n <svg\n class=\"validation-errors-list__arrow\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8Z\"\n />\n </svg>\n </summary>\n\n <div class=\"validation-errors-list__items\">\n <button\n *ngFor=\"let error of validationErrors\"\n class=\"validation-errors-list__item\"\n [class.warning]=\"error.severity === 'WARNING'\"\n (click)=\"onValidationErrorClick(error.elementId)\"\n >\n <span\n class=\"validation-errors-list__icon\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ error.severity === 'WARNING' ? '!' : '!' }}</span\n >\n\n <span class=\"validation-errors-list__details\">\n <span class=\"validation-errors-list__element\">{{\n error.elementName || error.elementId\n }}</span>\n\n <span\n class=\"validation-errors-list__reason\"\n [class.warning]=\"error.severity === 'WARNING'\"\n >{{ getValidationErrorMessage(error) }}</span\n >\n </span>\n </button>\n </div>\n </details>\n </div>\n </div>\n\n <div class=\"bpmn__modeler-panel\" #modelerPanel></div>\n </div>\n\n <div\n class=\"bpmn__modeler\"\n [style.display]=\"obs.isReadOnlyProcess || !obs.hasEditPermissions ? 'flex' : 'none'\"\n >\n <div class=\"bpmn__modeler-canvas\" #viewer>\n <cds-tooltip\n align=\"top-right\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.markers.toggleTooltip' | translate\"\n class=\"markers-visibility-toggle-wrapper\"\n >\n <button\n type=\"button\"\n class=\"markers-visibility-toggle\"\n (click)=\"toggleMarkerVisibility()\"\n [attr.data-test-id]=\"testIds.markersVisibilityToggle\"\n >\n <svg cdsIcon=\"view\" *ngIf=\"$markersVisible()\" size=\"16\"></svg>\n <svg cdsIcon=\"view--off\" *ngIf=\"!$markersVisible()\" size=\"16\"></svg>\n <span class=\"markers-visibility-toggle__label\">\n {{\n ($markersVisible()\n ? 'processManagement.markers.hide'\n : 'processManagement.markers.show'\n ) | translate\n }}\n </span>\n </button>\n </cds-tooltip>\n </div>\n\n <div class=\"bpmn__modeler-panel bpmn__modeler-panel--read-only\" #viewerPanel></div>\n </div>\n</div>\n\n<valtimo-process-link-modal></valtimo-process-link-modal>\n\n<valtimo-confirmation-modal\n [showModalSubject$]=\"showWarningConfirmationModal$\"\n [titleTranslationKey]=\"'processManagement.warningConfirmation.title'\"\n [contentTranslationKey]=\"'processManagement.warningConfirmation.content'\"\n [confirmButtonTextTranslationKey]=\"'processManagement.warningConfirmation.confirm'\"\n [cancelButtonTextTranslationKey]=\"'interface.cancel'\"\n (confirmEvent)=\"onWarningConfirmationConfirm()\"\n (cancelEvent)=\"onWarningConfirmationCancel()\"\n></valtimo-confirmation-modal>\n\n<ng-container renderInPageHeader [fullWidth]=\"true\">\n <ng-template>\n <div\n class=\"page-header-actions\"\n *ngIf=\"{\n processDefinitionVersionsListItems: processDefinitionVersionsListItems$ | async,\n loading: loading$ | async,\n changesPending: changesPending$ | async,\n isReadOnlyProcess: isReadOnlyProcess$ | async,\n isSystemProcess: isSystemProcess$ | async,\n compactMode: compactMode$ | async,\n creatingNewProcess: creatingNewProcess$ | async,\n context: context$ | async,\n hasEditPermissions: hasEditPermissions$ | async,\n draft: draft$ | async,\n canInitializeDocument: canInitializeDocument$ | async,\n startableByUser: startableByUser$ | async,\n updatingProcessDefinitionCaseDefinition: updatingProcessDefinitionCaseDefinition$ | async,\n } as actionsObs\"\n >\n <div class=\"page-header-actions__left-container\">\n <cds-dropdown\n *ngIf=\"!actionsObs.creatingNewProcess && actionsObs?.context === 'independent'\"\n class=\"page-header-actions__version-dropdown\"\n [attr.data-test-id]=\"testIds.versionDropdown\"\n (selected)=\"selectedVersionChange($event)\"\n [disabled]=\"\n actionsObs.loading ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 0 ||\n (actionsObs.processDefinitionVersionsListItems || []).length === 1\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n >\n <cds-dropdown-list\n [items]=\"actionsObs.processDefinitionVersionsListItems || []\"\n ></cds-dropdown-list>\n </cds-dropdown>\n\n <div\n class=\"page-header-actions__tags\"\n *ngIf=\"\n actionsObs.isReadOnlyProcess ||\n !actionsObs.hasEditPermissions ||\n actionsObs.isSystemProcess\n \"\n >\n <cds-tag\n *ngIf=\"actionsObs.isReadOnlyProcess || !actionsObs.hasEditPermissions\"\n type=\"blue\"\n [attr.data-test-id]=\"testIds.readOnlyTag\"\n >{{ 'processManagement.readOnly' | translate }}</cds-tag\n >\n\n <cds-tag\n *ngIf=\"actionsObs.isSystemProcess\"\n type=\"red\"\n [attr.data-test-id]=\"testIds.systemProcessTag\"\n >{{ 'processManagement.systemProcess' | translate }}</cds-tag\n >\n </div>\n\n <div class=\"page-header-actions__toggles\">\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processManagement.draftTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.draft' | translate\"\n [offText]=\"'processManagement.draft' | translate\"\n [checked]=\"actionsObs.draft\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n (checkedChange)=\"onProcessToggleChange('draft', $event)\"\n [attr.data-test-id]=\"testIds.draftToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n @if (actionsObs.context === 'case') {\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.processCreatesCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.canInitializeDocument' | translate\"\n [offText]=\"'processManagement.canInitializeDocument' | translate\"\n [checked]=\"actionsObs.canInitializeDocument\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('canInitializeDocument', $event)\"\n [attr.data-test-id]=\"testIds.startsCaseToggle\"\n ></cds-toggle>\n </cds-tooltip>\n\n <cds-tooltip\n align=\"bottom-left\"\n [autoAlign]=\"true\"\n [description]=\"'processCaseConnection.startableWithinCaseTooltip' | translate\"\n >\n <cds-toggle\n size=\"sm\"\n [onText]=\"'processManagement.startableByUser' | translate\"\n [offText]=\"'processManagement.startableByUser' | translate\"\n [checked]=\"actionsObs.startableByUser\"\n [disabled]=\"\n actionsObs.loading ||\n actionsObs.updatingProcessDefinitionCaseDefinition ||\n !actionsObs.hasEditPermissions\n \"\n (checkedChange)=\"onProcessToggleChange('startableByUser', $event)\"\n [attr.data-test-id]=\"testIds.startableByUserToggle\"\n ></cds-toggle\n ></cds-tooltip>\n }\n </div>\n </div>\n\n <div class=\"page-header-actions__buttons\">\n <v-overflow-menu class=\"overflow-button\" placement=\"bottom-end\">\n <v-overflow-menu-trigger\n overflowTrigger\n [attr.data-test-id]=\"testIds.moreButton\"\n ></v-overflow-menu-trigger>\n <v-overflow-menu-option\n *ngIf=\"actionsObs.context !== 'case'\"\n [disabled]=\"actionsObs.loading\"\n [attr.data-test-id]=\"testIds.exportOption\"\n (selected)=\"export()\"\n >{{ 'interface.export' | translate }}\n </v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"actionsObs.loading\"\n (selected)=\"exportBpmn(actionsObs.isReadOnlyProcess)\"\n >{{ 'processManagement.exportBpmn' | translate }}\n </v-overflow-menu-option>\n </v-overflow-menu>\n\n <button\n *ngIf=\"actionsObs.context === 'case'\"\n cdsButton=\"tertiary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n (click)=\"navigateBack(null)\"\n >\n {{ 'interface.back' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"return\" size=\"16\"></svg>\n </button>\n\n <button\n cdsButton=\"secondary\"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [disabled]=\"actionsObs.loading || !actionsObs.hasEditPermissions\"\n [attr.data-test-id]=\"testIds.validateButton\"\n (click)=\"validateProcessDefinition(actionsObs.isReadOnlyProcess)\"\n >\n {{ 'processManagement.validate' | translate }}\n <svg class=\"cds--btn__icon\" cdsIcon=\"list--checked\" size=\"16\"></svg>\n </button>\n\n <button\n [disabled]=\"!actionsObs.changesPending || !actionsObs.hasEditPermissions\"\n cdsButton=\"primary\"\n (click)=\"\n !actionsObs.creatingNewProcess\n ? deployChanges(actionsObs.isReadOnlyProcess)\n : deployNewProcessDefinition()\n \"\n [size]=\"actionsObs.compactMode ? 'sm' : 'md'\"\n [attr.data-test-id]=\"testIds.deployButton\"\n >\n {{ 'processManagement.deploy' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"deploy\" size=\"16\"></svg>\n </button>\n </div>\n </div>\n </ng-template>\n</ng-container>\n", styles: [":host{display:flex;flex-direction:column;gap:24px}.bpmn__container{position:relative;background-color:var(--cds-layer);width:100%}.bpmn__modeler{width:100%;height:100%;flex-direction:row;padding:1px;box-sizing:border-box;outline:1px solid var(--cds-border-subtle);outline-offset:-1px}.bpmn__modeler ::ng-deep .djs-container>svg{outline:none!important}.bpmn__modeler-canvas{width:100%;position:relative}.bpmn__modeler-canvas .markers-visibility-toggle-wrapper{position:absolute;bottom:55px;right:15px;z-index:100}.bpmn__modeler-canvas .markers-visibility-toggle{display:flex;align-items:center;gap:6px;height:32px;padding:0 10px;border:none;border-radius:2px;background-color:var(--cds-layer);cursor:pointer;color:var(--cds-text-secondary);transition:background-color .15s,color .15s}.bpmn__modeler-canvas .markers-visibility-toggle:hover{background-color:var(--cds-layer-hover);color:var(--cds-text-primary)}.bpmn__modeler-canvas .markers-visibility-toggle:focus{outline:2px solid var(--cds-focus);outline-offset:-2px}.bpmn__modeler-canvas .markers-visibility-toggle__label{font-size:12px;white-space:nowrap}.bpmn__modeler-panel{width:481px;padding-left:1px;box-shadow:inset 1px 0 0 0 var(--cds-border-subtle);box-sizing:border-box}.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry input,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry select,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry textarea,.bpmn__modeler-panel--read-only ::ng-deep .bio-properties-panel-entry .bio-properties-panel-feel-editor-container{opacity:.65}.bpmn__loading{width:100%;height:100%;display:flex;justify-content:center;align-items:center;background-color:var(--cds-background-selected)}.page-header-actions{display:flex;width:100%;justify-content:space-between;align-items:center;gap:16px}.page-header-actions__left-container{display:flex;gap:16px;align-items:center}.page-header-actions__tags{display:flex;gap:8px;align-items:center}.page-header-actions__version-dropdown{width:150px}.page-header-actions__toggles{display:flex;gap:16px;align-items:center}.page-header-actions__toggles ::ng-deep cds-toggle{display:inline-flex;align-items:center}.page-header-actions__toggles ::ng-deep .cds--toggle__label-text{display:none}.page-header-actions__buttons{display:flex;gap:16px;align-items:center;flex-shrink:0}.valtimo-process-builder__header{display:flex;justify-content:space-between}.valtimo-process-builder__actions>*:not(:last-child){margin-right:8px}.validation-errors-list{position:absolute;top:8px;right:8px;z-index:10;width:320px;max-height:250px;overflow-y:auto;border:1px solid var(--cds-border-subtle);background-color:var(--cds-layer);border-radius:4px;box-shadow:0 2px 6px #00000026}.validation-errors-list details[open]>summary .validation-errors-list__arrow{transform:rotate(90deg)}.validation-errors-list__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;font-size:14px;font-weight:600;color:var(--cds-support-error, #da1e28);cursor:pointer;list-style:none;-webkit-user-select:none;user-select:none}.validation-errors-list__header::-webkit-details-marker{display:none}.validation-errors-list__arrow{display:inline-flex;width:16px;height:16px;fill:currentColor;transition:transform .15s ease;flex-shrink:0}.validation-errors-list__items{display:flex;flex-direction:column;border-top:1px solid var(--cds-border-subtle)}.validation-errors-list__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border:none;border-bottom:1px solid var(--cds-border-subtle);border-left:3px solid var(--cds-support-error, #da1e28);background-color:var(--cds-notification-error-background, #fff1f1);text-align:left;cursor:pointer;font-family:inherit}.validation-errors-list__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}.validation-errors-list__item:last-child{border-bottom:none}.validation-errors-list__item:hover{filter:brightness(.97)}.validation-errors-list__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;margin-top:1px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}.validation-errors-list__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}.validation-errors-list__details{display:flex;flex-direction:column;gap:2px}.validation-errors-list__element{font-size:13px;font-weight:600;color:var(--cds-text-primary)}.validation-errors-list__reason{font-size:13px;color:var(--cds-text-primary)}::ng-deep .highlight-overlay-error:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-error, #da1e28)!important}::ng-deep .highlight-overlay-warning:not(.djs-connection) .djs-visual>:nth-child(1){stroke:var(--cds-support-warning, #f1c21b)!important}::ng-deep .djs-overlay-validation-error{overflow:visible!important;pointer-events:all!important}::ng-deep .validation-error-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-error-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-error-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-error, #da1e28);color:var(--cds-support-error, #da1e28);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-error-overlay:hover ::ng-deep .validation-error-overlay__text,::ng-deep .validation-error-overlay--active ::ng-deep .validation-error-overlay__text{display:block}::ng-deep .validation-warning-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .validation-warning-overlay__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);font-size:14px;font-weight:700;line-height:0}::ng-deep .validation-warning-overlay__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .validation-warning-overlay:hover ::ng-deep .validation-warning-overlay__text,::ng-deep .validation-warning-overlay--active ::ng-deep .validation-warning-overlay__text{display:block}::ng-deep .djs-overlay-activity-marker{overflow:visible!important;pointer-events:all!important}::ng-deep .activity-marker-overlay{display:flex;gap:2px}::ng-deep .activity-marker-badge{position:relative;cursor:pointer;pointer-events:all}::ng-deep .activity-marker-badge__icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:14px;font-weight:700;line-height:0}::ng-deep .activity-marker-badge__text{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .activity-marker-badge:hover ::ng-deep .activity-marker-badge__text,::ng-deep .activity-marker-badge--active ::ng-deep .activity-marker-badge__text{display:block}::ng-deep .activity-marker-badge--process-link ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--process-link ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-support-info, #0043ce);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .activity-marker-badge--execution-listener ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-purple-60, #8a3ffc);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--execution-listener ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-purple-60, #8a3ffc);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .activity-marker-badge--task-listener ::ng-deep .activity-marker-badge__icon{background-color:var(--cds-teal-50, #009d9a);color:var(--cds-text-on-color, #ffffff)}::ng-deep .activity-marker-badge--task-listener ::ng-deep .activity-marker-badge__text{border:1px solid var(--cds-teal-50, #009d9a);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff)}::ng-deep .validation-errors-panel{padding:8px 12px;display:flex;flex-direction:column;gap:8px}::ng-deep .validation-errors-panel__item{display:flex;align-items:flex-start;gap:8px;padding:8px 12px;background-color:var(--cds-notification-error-background, #fff1f1);border-left:3px solid var(--cds-support-error, #da1e28)}::ng-deep .validation-errors-panel__item.warning{background-color:var(--cds-notification-warning-background, #fdf6dd);border-left-color:var(--cds-support-warning, #f1c21b)}::ng-deep .validation-errors-panel__icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;min-width:18px;border-radius:50%;background-color:var(--cds-support-error, #da1e28);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700;line-height:0}::ng-deep .validation-errors-panel__icon.warning{background-color:var(--cds-support-warning, #f1c21b);color:var(--cds-text-primary, #161616)}::ng-deep .validation-errors-panel__reason{color:var(--cds-text-primary, #161616);font-size:13px;line-height:18px}::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-input,::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-textfield,::ng-deep .bio-properties-panel-entry.validation-error-field .bio-properties-panel-feel-editor-container{border:2px solid var(--cds-support-error, #da1e28)!important;background-color:var(--cds-notification-error-background, #fff1f1)!important}::ng-deep .expression-editor-param input.validation-error-param,::ng-deep input.expression-editor-input.validation-error-param,::ng-deep .expression-editor-technical input.validation-error-param,::ng-deep .expression-editor-technical textarea.validation-error-param{border:2px solid #da1e28!important;background-color:#fff1f1!important;border-color:#da1e28!important}::ng-deep .process-link-properties-panel{padding:8px 12px 16px;width:100%;display:flex;flex-direction:column;gap:16px}::ng-deep .process-link-properties-panel__buttons,::ng-deep .process-link-properties-panel__header{width:100%;display:flex;gap:16px}::ng-deep .process-link-properties-panel__header{align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--cds-border-subtle-00);padding-bottom:8px;padding-left:8px;padding-right:8px}::ng-deep .process-link-properties-panel__title{color:var(--cds-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px;margin:auto 0}::ng-deep .process-link-properties-panel__title-container{display:flex;flex-direction:column;gap:4px}::ng-deep .process-link-properties-panel__title-container :last-child{color:var(--cds-text-helper);font-style:italic}::ng-deep .process-link-properties-panel .cds--tag{margin:0}::ng-deep .process-link-properties-panel .cds--btn{flex-shrink:1;width:100%;display:flex;align-items:center}::ng-deep .djs-overlay-autofilled-indicator{overflow:visible!important;pointer-events:all!important}::ng-deep .autofilled-indicator-overlay{position:relative;cursor:pointer;pointer-events:all}::ng-deep .autofilled-indicator-overlay .autofilled-indicator-icon{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff);font-size:14px;font-weight:700;line-height:0}::ng-deep .autofilled-indicator-overlay .autofilled-indicator-tooltip{display:none;position:absolute;bottom:100%;left:50%;transform:translate(-50%);margin-bottom:4px;border:1px solid var(--cds-support-info, #0043ce);color:var(--cds-text-primary, #161616);background-color:var(--cds-layer, #ffffff);padding:4px 8px;border-radius:2px;font-size:14px;white-space:nowrap;max-width:350px;overflow:hidden;text-overflow:ellipsis;z-index:10}::ng-deep .autofilled-indicator-overlay:hover .autofilled-indicator-tooltip{display:block}::ng-deep .autofilled-notification-panel{display:flex;align-items:center;gap:8px;padding:8px 12px;background-color:var(--cds-layer-02, #f4f4f4);border-left:3px solid var(--cds-support-info, #0043ce);margin-bottom:8px}::ng-deep .autofilled-notification-panel__icon{display:flex;align-items:center;justify-content:center;min-width:18px;height:18px;border-radius:50%;background-color:var(--cds-support-info, #0043ce);color:var(--cds-text-on-color, #ffffff);font-size:12px;font-weight:700}::ng-deep .autofilled-notification-panel__message{flex:1;font-size:12px;color:var(--cds-text-primary, #161616)}::ng-deep .autofilled-notification-panel__dismiss{background:none;border:none;cursor:pointer;padding:2px 6px;font-size:16px;color:var(--cds-text-secondary, #525252);line-height:1}::ng-deep .autofilled-notification-panel__dismiss:hover{color:var(--cds-text-primary, #161616)}\n/*!\n * Copyright 2015-2026 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
3202
+ }], ctorParameters: () => [{ type: i1$1.BreadcrumbService }, { type: i2$2.IconService }, { type: i3.NGXLogger }, { type: i1$1.ModalService }, { type: i2.GlobalNotificationService }, { type: i1$1.PageHeaderService }, { type: i1$1.PageTitleService }, { type: i5.ProcessLinkService }, { type: i5.ProcessLinkStateService }, { type: ProcessManagementEditorService }, { type: ProcessManagementService }, { type: i7.ProcessService }, { type: i8.ActivatedRoute }, { type: i8.Router }, { type: i9.TranslateService }, { type: i10.PluginTranslationService }, { type: i2.EditPermissionsService }, { type: i5.ProcessLinkBuildingBlockApiService }, { type: ProcessBeanService }], propDecorators: { modelerElementRef: [{
1955
3203
  type: ViewChild,
1956
3204
  args: ['modeler', { static: false }]
1957
3205
  }], modelerPanelElementRef: [{
@@ -2070,7 +3318,7 @@ class ProcessManagementListComponent {
2070
3318
  this.processToDelete$.next(process.processDefinition);
2071
3319
  this.showDeleteModal$.next(true);
2072
3320
  }
2073
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementListComponent, deps: [{ token: i2$2.IconService }, { token: i2$1.GlobalNotificationService }, { token: ProcessManagementService }, { token: ProcessManagementStateService }, { token: i9.TranslateService }, { token: i2$1.EnvironmentService }, { token: i8.ActivatedRoute }, { token: i2$1.EditPermissionsService }], target: i0.ɵɵFactoryTarget.Component }); }
3321
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementListComponent, deps: [{ token: i2$2.IconService }, { token: i2.GlobalNotificationService }, { token: ProcessManagementService }, { token: ProcessManagementStateService }, { token: i9.TranslateService }, { token: i2.EnvironmentService }, { token: i8.ActivatedRoute }, { token: i2.EditPermissionsService }], target: i0.ɵɵFactoryTarget.Component }); }
2074
3322
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ProcessManagementListComponent, isStandalone: true, selector: "valtimo-process-management-list", outputs: { processSelected: "processSelected" }, ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [actionItems]=\"ACTION_ITEMS\"\n [showActionItems]=\"hasEditPermissions$ | async\"\n [fields]=\"FIELDS\"\n [header]=\"false\"\n [items]=\"processDefinitions$ | async\"\n [loading]=\"loading$ | async\"\n (rowClicked)=\"editProcessDefinition($event)\"\n>\n <ng-container carbonToolbarContent>\n <button\n *ngIf=\"hasEditPermissions$ | async\"\n cdsButton=\"ghost\"\n iconOnly=\"true\"\n [attr.data-test-id]=\"testIds.uploadButton\"\n (click)=\"openModal()\"\n >\n <svg cdsIcon=\"upload\" size=\"16\"></svg>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"createProcess\"></ng-container>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"createProcess\"\n [title]=\"'processManagement.noResults.title' | translate\"\n [description]=\"'processManagement.noResults.' + $context() + 'Description' | translate\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"processManagement.deleteModal.content\"\n [outputOnConfirm]=\"processToDelete$ | async\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"processManagement.deleteModal.title\"\n (confirmEvent)=\"onDeleteConfirm($event)\"\n></valtimo-confirmation-modal>\n\n<ng-template #createProcess>\n <button *ngIf=\"hasEditPermissions$ | async\" cdsButton=\"primary\" [attr.data-test-id]=\"testIds.createProcessButton\" (click)=\"onCreateProcess()\">\n {{ 'processManagement.createProcess' | translate }}\n\n <svg cdsIcon=\"add\" size=\"16\" class=\"cds--btn__icon\"></svg>\n </button>\n</ng-template>\n", styles: ["/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i6.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$2.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: CarbonListModule }, { kind: "component", type: i1$1.CarbonListComponent, selector: "valtimo-carbon-list", inputs: ["items", "fields", "tableTranslations", "paginatorConfig", "pagination", "loading", "skeletonRowCount", "actions", "actionItems", "showActionItems", "header", "hideColumnHeader", "initialSortState", "sortState", "isSearchable", "initialSearchValue", "searchDebounceMs", "invalidSearchFields", "searchFields", "enableSingleSelection", "lastColumnTemplate", "paginationIdentifier", "showSelectionColumn", "striped", "hideToolbar", "lockedTooltipTranslationKey", "movingRowsEnabled", "dragAndDrop", "dragAndDropDisabled", "expandedRowTemplate", "expandedRowKey", "trackByKey"], outputs: ["rowClicked", "paginationClicked", "paginationSet", "search", "sortChanged", "moveRow", "itemsReordered"] }, { kind: "component", type: i1$1.CarbonNoResultsComponent, selector: "valtimo-no-results", inputs: ["action", "description", "illustration", "title", "smallPadding", "collapseVertically", "alwaysRenderVertically"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$2.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i9.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ConfirmationModalModule }, { kind: "component", type: i1$1.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2075
3323
  }
2076
3324
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementListComponent, decorators: [{
@@ -2084,7 +3332,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2084
3332
  TranslateModule,
2085
3333
  ConfirmationModalModule,
2086
3334
  ], template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<valtimo-carbon-list\n [actionItems]=\"ACTION_ITEMS\"\n [showActionItems]=\"hasEditPermissions$ | async\"\n [fields]=\"FIELDS\"\n [header]=\"false\"\n [items]=\"processDefinitions$ | async\"\n [loading]=\"loading$ | async\"\n (rowClicked)=\"editProcessDefinition($event)\"\n>\n <ng-container carbonToolbarContent>\n <button\n *ngIf=\"hasEditPermissions$ | async\"\n cdsButton=\"ghost\"\n iconOnly=\"true\"\n [attr.data-test-id]=\"testIds.uploadButton\"\n (click)=\"openModal()\"\n >\n <svg cdsIcon=\"upload\" size=\"16\"></svg>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"createProcess\"></ng-container>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"createProcess\"\n [title]=\"'processManagement.noResults.title' | translate\"\n [description]=\"'processManagement.noResults.' + $context() + 'Description' | translate\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"processManagement.deleteModal.content\"\n [outputOnConfirm]=\"processToDelete$ | async\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"processManagement.deleteModal.title\"\n (confirmEvent)=\"onDeleteConfirm($event)\"\n></valtimo-confirmation-modal>\n\n<ng-template #createProcess>\n <button *ngIf=\"hasEditPermissions$ | async\" cdsButton=\"primary\" [attr.data-test-id]=\"testIds.createProcessButton\" (click)=\"onCreateProcess()\">\n {{ 'processManagement.createProcess' | translate }}\n\n <svg cdsIcon=\"add\" size=\"16\" class=\"cds--btn__icon\"></svg>\n </button>\n</ng-template>\n", styles: ["/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
2087
- }], ctorParameters: () => [{ type: i2$2.IconService }, { type: i2$1.GlobalNotificationService }, { type: ProcessManagementService }, { type: ProcessManagementStateService }, { type: i9.TranslateService }, { type: i2$1.EnvironmentService }, { type: i8.ActivatedRoute }, { type: i2$1.EditPermissionsService }], propDecorators: { processSelected: [{
3335
+ }], ctorParameters: () => [{ type: i2$2.IconService }, { type: i2.GlobalNotificationService }, { type: ProcessManagementService }, { type: ProcessManagementStateService }, { type: i9.TranslateService }, { type: i2.EnvironmentService }, { type: i8.ActivatedRoute }, { type: i2.EditPermissionsService }], propDecorators: { processSelected: [{
2088
3336
  type: Output
2089
3337
  }] } });
2090
3338
 
@@ -2103,45 +3351,206 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2103
3351
  * See the License for the specific language governing permissions and
2104
3352
  * limitations under the License.
2105
3353
  */
3354
+ var UPLOAD_STEP;
3355
+ (function (UPLOAD_STEP) {
3356
+ UPLOAD_STEP["FILE_SELECT"] = "fileSelect";
3357
+ UPLOAD_STEP["REVIEW"] = "review";
3358
+ UPLOAD_STEP["SUMMARY"] = "summary";
3359
+ })(UPLOAD_STEP || (UPLOAD_STEP = {}));
2106
3360
  class ProcessManagementUploadComponent {
2107
- constructor(formBuilder, notificationService, processManagementService, processManagementStateService, processLinkService, translateService) {
3361
+ constructor(formBuilder, notificationService, processManagementService, processManagementStateService, processLinkService, translateService, changeDetectorRef) {
2108
3362
  this.formBuilder = formBuilder;
2109
3363
  this.notificationService = notificationService;
2110
3364
  this.processManagementService = processManagementService;
2111
3365
  this.processManagementStateService = processManagementStateService;
2112
3366
  this.processLinkService = processLinkService;
2113
3367
  this.translateService = translateService;
3368
+ this.changeDetectorRef = changeDetectorRef;
2114
3369
  this.modalOpen$ = this.processManagementStateService.openModal$;
2115
3370
  this.showReplaceConfirmationModal$ = new BehaviorSubject(false);
2116
3371
  this.replaceModalContent = '';
2117
3372
  this._conflictingProcessDefinitionId = null;
2118
- this.ACCEPTED_FILES = ['bpmn'];
3373
+ // Extensions need a leading dot: the file dialog only filters on valid accept tokens, and Carbon
3374
+ // matches a dropped file on its dot-prefixed extension
3375
+ this.ACCEPTED_FILES = ['.bpmn', '.zip'];
3376
+ this.UPLOAD_STEP = UPLOAD_STEP;
3377
+ this.testIds = PROCESS_MANAGEMENT_UPLOAD_TEST_IDS;
2119
3378
  this.form = this.formBuilder.group({
2120
3379
  file: this.formBuilder.control(new Set(), [Validators.required]),
2121
3380
  });
2122
- this.fileSelected$ = this.form.get('file')?.valueChanges.pipe(startWith(null), map$1(value => !!(value instanceof Set && value.size > 0)));
3381
+ // The current control value is read instead of the emitted one, so this also reports the selection
3382
+ // correctly when the footer is re-created after going back to the file select step
3383
+ this.fileSelected$ = this.form.get('file')?.valueChanges.pipe(startWith(null), map$1(() => {
3384
+ const value = this.form.get('file')?.value;
3385
+ return !!(value instanceof Set && value.size > 0);
3386
+ }));
3387
+ this.activeStep$ = new BehaviorSubject(UPLOAD_STEP.FILE_SELECT);
3388
+ this.preview$ = new BehaviorSubject(null);
3389
+ this.missingReferences$ = new BehaviorSubject([]);
3390
+ this.importing$ = new BehaviorSubject(false);
3391
+ /**
3392
+ * Leaving the file select step destroys cds-file-uploader, which empties the form control: every
3393
+ * cds-file emits (remove) from its own ngOnDestroy. So the selection is kept here to import it
3394
+ * afterwards, and to restore it when the user goes back.
3395
+ */
3396
+ this._selectedFile = null;
3397
+ this._selectedFileItems = null;
2123
3398
  }
2124
3399
  closeModal() {
2125
3400
  this.processManagementStateService.closeModal();
2126
3401
  setTimeout(() => {
2127
3402
  this.form.reset();
3403
+ this.activeStep$.next(UPLOAD_STEP.FILE_SELECT);
3404
+ this.preview$.next(null);
3405
+ this.missingReferences$.next([]);
3406
+ this.importing$.next(false);
3407
+ this._selectedFile = null;
3408
+ this._selectedFileItems = null;
2128
3409
  }, CARBON_CONSTANTS.modalAnimationMs);
2129
3410
  }
2130
3411
  uploadProcessBpmn() {
2131
- const bpmnFile = this.form.value?.file?.values()?.next()?.value?.file;
2132
- if (!bpmnFile)
3412
+ const file = this.form.value?.file?.values()?.next()?.value?.file;
3413
+ if (!file)
2133
3414
  return;
3415
+ // A package of an earlier selection must not be imported when another file is uploaded
3416
+ this._selectedFile = null;
3417
+ this._selectedFileItems = null;
3418
+ if (this.isZip(file)) {
3419
+ this.previewProcessPackage(file);
3420
+ return;
3421
+ }
2134
3422
  if (this.processManagementService.$context() === 'case') {
2135
- this.uploadForCase(bpmnFile);
3423
+ this.uploadForCase(file);
2136
3424
  }
2137
3425
  else {
2138
- this.uploadIndependent(bpmnFile);
3426
+ this.uploadIndependent(file);
3427
+ }
3428
+ }
3429
+ importProcessPackage() {
3430
+ const existingProcessDefinitionKeys = this.preview$.value?.existingProcessDefinitionKeys ?? [];
3431
+ // Ask before replacing, the same way uploading a single bpmn file does
3432
+ if (existingProcessDefinitionKeys.length > 0) {
3433
+ this.showReplaceConfirmation(this.translateService.instant('processManagement.upload.replaceContentWithDuplicates', {
3434
+ duplicates: existingProcessDefinitionKeys.join(', '),
3435
+ }));
3436
+ return;
3437
+ }
3438
+ this.executeImportProcessPackage();
3439
+ }
3440
+ showReplaceConfirmation(content) {
3441
+ this.replaceModalContent = content;
3442
+ // The content is bound from a plain field, so this OnPush component has to be checked again.
3443
+ // The bpmn flow sets it from an http error callback, which does not mark this view dirty itself.
3444
+ this.changeDetectorRef.markForCheck();
3445
+ this.showReplaceConfirmationModal$.next(true);
3446
+ }
3447
+ executeImportProcessPackage() {
3448
+ const file = this._selectedFile;
3449
+ if (!file)
3450
+ return;
3451
+ this.importing$.next(true);
3452
+ this.processManagementService
3453
+ .importProcessDefinition(this.toFormData(file), this._pluginConfigurationMapping?.getMappings())
3454
+ .pipe(take(1))
3455
+ .subscribe({
3456
+ next: result => {
3457
+ this.importing$.next(false);
3458
+ this.processManagementStateService.reloadDefinitions();
3459
+ if (result.missingReferences.length > 0) {
3460
+ // Keep the summary on screen: a toast disappears before it can be read
3461
+ this.missingReferences$.next(result.missingReferences);
3462
+ this.activeStep$.next(UPLOAD_STEP.SUMMARY);
3463
+ return;
3464
+ }
3465
+ this.notificationService.showNotification({
3466
+ type: 'success',
3467
+ title: this.translateService.instant('processManagement.upload.success'),
3468
+ });
3469
+ this.closeModal();
3470
+ },
3471
+ error: () => {
3472
+ this.importing$.next(false);
3473
+ this.notificationService.showNotification({
3474
+ type: 'error',
3475
+ title: this.translateService.instant('processManagement.upload.failure'),
3476
+ });
3477
+ },
3478
+ });
3479
+ }
3480
+ onBackClick() {
3481
+ this.activeStep$.next(UPLOAD_STEP.FILE_SELECT);
3482
+ // Restore the selection the file uploader dropped when it was destroyed
3483
+ if (this._selectedFileItems) {
3484
+ this.form.get('file')?.setValue(this._selectedFileItems);
2139
3485
  }
2140
3486
  }
3487
+ getMissingReferenceGroups(missingReferences) {
3488
+ return missingReferences.reduce((groups, missingReference) => {
3489
+ const group = groups.find(({ type }) => type === missingReference.type);
3490
+ if (group) {
3491
+ group.references = [...new Set([...group.references, missingReference.reference])];
3492
+ return groups;
3493
+ }
3494
+ return [...groups, { type: missingReference.type, references: [missingReference.reference] }];
3495
+ }, []);
3496
+ }
3497
+ getReplacedElementGroups(elementsToReplace) {
3498
+ return (elementsToReplace ?? []).reduce((groups, element) => {
3499
+ const group = groups.find(({ type }) => type === element.type);
3500
+ if (group) {
3501
+ group.keys = [...new Set([...group.keys, element.key])];
3502
+ return groups;
3503
+ }
3504
+ return [...groups, { type: element.type, keys: [element.key] }];
3505
+ }, []);
3506
+ }
3507
+ previewProcessPackage(file) {
3508
+ const fileItem = this.form.value?.file?.values()?.next()?.value;
3509
+ this.processManagementService
3510
+ .previewProcessDefinitionImport(this.toFormData(file))
3511
+ .pipe(take(1))
3512
+ .subscribe({
3513
+ next: preview => {
3514
+ this._selectedFile = file;
3515
+ this._selectedFileItems = this.form.value?.file ?? null;
3516
+ this.preview$.next(preview);
3517
+ this.missingReferences$.next(preview.missingReferences);
3518
+ // Nothing to review, so the package can be imported straight away. Elements that will be
3519
+ // replaced are shown in the review step first, so the replacement is a conscious choice.
3520
+ if (preview.pluginConfigurations.length === 0 &&
3521
+ preview.missingReferences.length === 0 &&
3522
+ preview.elementsToReplace.length === 0) {
3523
+ this.importProcessPackage();
3524
+ return;
3525
+ }
3526
+ this.activeStep$.next(UPLOAD_STEP.REVIEW);
3527
+ },
3528
+ error: () => {
3529
+ if (fileItem) {
3530
+ fileItem.invalid = true;
3531
+ fileItem.invalidTitle = this.translateService.instant('processManagement.upload.invalidZip.title');
3532
+ fileItem.invalidText = this.translateService.instant('processManagement.upload.invalidZip.text');
3533
+ }
3534
+ },
3535
+ });
3536
+ }
3537
+ isZip(file) {
3538
+ return file.name.toLowerCase().endsWith('.zip');
3539
+ }
3540
+ toFormData(file) {
3541
+ const formData = new FormData();
3542
+ formData.append('file', file, file.name);
3543
+ return formData;
3544
+ }
2141
3545
  confirmReplace() {
2142
3546
  const processDefinitionId = this._conflictingProcessDefinitionId;
2143
3547
  this.replaceModalContent = '';
2144
3548
  this._conflictingProcessDefinitionId = null;
3549
+ // A selected package is only set while importing a zip, which replaces through the import itself
3550
+ if (this._selectedFile) {
3551
+ this.executeImportProcessPackage();
3552
+ return;
3553
+ }
2145
3554
  if (!processDefinitionId) {
2146
3555
  this.notificationService.showNotification({
2147
3556
  type: 'error',
@@ -2192,11 +3601,9 @@ class ProcessManagementUploadComponent {
2192
3601
  error: (error) => {
2193
3602
  const isConflict = error instanceof HttpErrorResponse && error.status === 409;
2194
3603
  if (isConflict) {
2195
- const body = error
2196
- .error;
3604
+ const body = error.error;
2197
3605
  this._conflictingProcessDefinitionId = body?.processDefinitionId ?? null;
2198
- this.replaceModalContent = this.buildReplaceModalContent(body);
2199
- this.showReplaceConfirmationModal$.next(true);
3606
+ this.showReplaceConfirmation(this.buildReplaceModalContent(body));
2200
3607
  return;
2201
3608
  }
2202
3609
  this.notificationService.showNotification({
@@ -2221,11 +3628,9 @@ class ProcessManagementUploadComponent {
2221
3628
  error: (error) => {
2222
3629
  const isConflict = error instanceof HttpErrorResponse && error.status === 409;
2223
3630
  if (isConflict) {
2224
- const body = error
2225
- .error;
3631
+ const body = error.error;
2226
3632
  this._conflictingProcessDefinitionId = body?.processDefinitionId ?? null;
2227
- this.replaceModalContent = this.buildReplaceModalContent(body);
2228
- this.showReplaceConfirmationModal$.next(true);
3633
+ this.showReplaceConfirmation(this.buildReplaceModalContent(body));
2229
3634
  return;
2230
3635
  }
2231
3636
  this.notificationService.showNotification({
@@ -2244,8 +3649,8 @@ class ProcessManagementUploadComponent {
2244
3649
  }
2245
3650
  return this.translateService.instant('processManagement.upload.replaceContent');
2246
3651
  }
2247
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementUploadComponent, deps: [{ token: i1$2.FormBuilder }, { token: i2$1.GlobalNotificationService }, { token: ProcessManagementService }, { token: ProcessManagementStateService }, { token: i5.ProcessLinkService }, { token: i9.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
2248
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ProcessManagementUploadComponent, isStandalone: true, selector: "valtimo-process-management-upload", ngImport: i0, template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal valtimoCdsModal [open]=\"modalOpen$ | async\" size=\"sm\" (close)=\"closeModal()\">\n <cds-modal-header showCloseButton=\"true\" (closeSelect)=\"closeModal()\">\n <h3 cdsModalHeaderHeading>{{ 'processManagement.upload.modalTitle' | translate }}</h3>\n </cds-modal-header>\n\n <section cdsModalContent [cdsLayer]=\"1\" [formGroup]=\"form\">\n <cds-file-uploader\n [accept]=\"ACCEPTED_FILES\"\n [buttonText]=\"'processManagement.upload.buttonText' | translate\"\n [description]=\"'processManagement.upload.description' | translate\"\n [multiple]=\"false\"\n [title]=\"'processManagement.upload.title' | translate\"\n buttonType=\"primary\"\n formControlName=\"file\"\n >\n </cds-file-uploader>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"closeModal()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"(fileSelected$ | async) === false\"\n (click)=\"uploadProcessBpmn()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n\n<valtimo-render-in-body>\n <valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"processManagement.upload.replaceConfirm\"\n confirmButtonType=\"danger\"\n cancelButtonTextTranslationKey=\"interface.cancel\"\n [content]=\"replaceModalContent\"\n [showModalSubject$]=\"showReplaceConfirmationModal$\"\n titleTranslationKey=\"processManagement.upload.replaceTitle\"\n (confirmEvent)=\"confirmReplace()\"\n (cancelEvent)=\"clearReplaceModal()\"\n ></valtimo-confirmation-modal>\n</valtimo-render-in-body>\n", styles: ["/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i9.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: FileUploaderModule }, { kind: "component", type: i2$2.FileUploader, selector: "cds-file-uploader, ibm-file-uploader", inputs: ["buttonText", "buttonType", "title", "description", "accept", "multiple", "skeleton", "size", "fileItemSize", "drop", "dropText", "fileUploaderId", "files", "disabled"], outputs: ["filesChange"] }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type: i2$2.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$2.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$2.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$2.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$2.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "ngmodule", type: LayerModule }, { kind: "directive", type: i2$2.LayerDirective, selector: "[cdsLayer], [ibmLayer]", inputs: ["ibmLayer", "cdsLayer"], exportAs: ["layer"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$2.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: ConfirmationModalModule }, { kind: "component", type: i1$1.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "component", type: RenderInBodyComponent, selector: "valtimo-render-in-body" }, { kind: "directive", type: ValtimoCdsModalDirective, selector: "[valtimoCdsModal]", inputs: ["minContentHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3652
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementUploadComponent, deps: [{ token: i1$2.FormBuilder }, { token: i2.GlobalNotificationService }, { token: ProcessManagementService }, { token: ProcessManagementStateService }, { token: i5.ProcessLinkService }, { token: i9.TranslateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3653
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ProcessManagementUploadComponent, isStandalone: true, selector: "valtimo-process-management-upload", viewQueries: [{ propertyName: "_pluginConfigurationMapping", first: true, predicate: PluginConfigurationMappingComponent, descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal valtimoCdsModal [open]=\"modalOpen$ | async\" size=\"sm\" (close)=\"closeModal()\">\n <cds-modal-header showCloseButton=\"true\" (closeSelect)=\"closeModal()\">\n <h3 cdsModalHeaderHeading>{{ 'processManagement.upload.modalTitle' | translate }}</h3>\n </cds-modal-header>\n\n <ng-container\n *ngIf=\"{\n activeStep: activeStep$ | async,\n preview: preview$ | async,\n missingReferences: missingReferences$ | async,\n importing: importing$ | async,\n } as obs\"\n >\n <section cdsModalContent [cdsLayer]=\"1\" [formGroup]=\"form\" [ngSwitch]=\"obs.activeStep\">\n <cds-file-uploader\n *ngSwitchCase=\"UPLOAD_STEP.FILE_SELECT\"\n [accept]=\"ACCEPTED_FILES\"\n [buttonText]=\"'processManagement.upload.buttonText' | translate\"\n [description]=\"'processManagement.upload.description' | translate\"\n [multiple]=\"false\"\n [title]=\"'processManagement.upload.title' | translate\"\n buttonType=\"primary\"\n formControlName=\"file\"\n [attr.data-test-id]=\"testIds.fileUploader\"\n >\n </cds-file-uploader>\n\n <div *ngSwitchCase=\"UPLOAD_STEP.REVIEW\" class=\"process-management-upload__review\">\n <ng-container *ngFor=\"let group of getMissingReferenceGroups(obs.missingReferences)\">\n <cds-inline-notification\n [notificationObj]=\"{\n type:\n group.type === 'SUB_PROCESS' || group.type === 'DECISION_DEFINITION'\n ? 'warning'\n : 'error',\n title:\n 'processManagement.upload.missingReference.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.missingReference.' + group.type + '.message'\n | translate) +\n ' ' +\n group.references.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n\n <ng-container\n *ngFor=\"let group of getReplacedElementGroups(obs.preview?.elementsToReplace ?? [])\"\n >\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title: 'processManagement.upload.willBeReplaced.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.willBeReplaced.' + group.type + '.message' | translate) +\n ' ' +\n group.keys.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n\n <ng-container *ngIf=\"obs.preview?.pluginConfigurations?.length\">\n <p class=\"process-management-upload__description\">\n {{ 'processManagement.upload.pluginsDescription' | translate }}\n </p>\n\n <valtimo-plugin-configuration-mapping\n [pluginConfigurations]=\"obs.preview.pluginConfigurations\"\n ></valtimo-plugin-configuration-mapping>\n </ng-container>\n </div>\n\n <div *ngSwitchCase=\"UPLOAD_STEP.SUMMARY\" class=\"process-management-upload__review\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'success',\n title: 'processManagement.upload.success' | translate,\n message: 'processManagement.upload.summaryDescription' | translate,\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n\n <ng-container *ngFor=\"let group of getMissingReferenceGroups(obs.missingReferences)\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title:\n 'processManagement.upload.missingReference.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.missingReference.' + group.type + '.message'\n | translate) +\n ' ' +\n group.references.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n </div>\n </section>\n\n <cds-modal-footer>\n <ng-container *ngIf=\"obs.activeStep === UPLOAD_STEP.FILE_SELECT\">\n <button\n cdsButton=\"secondary\"\n [attr.data-test-id]=\"testIds.cancelButton\"\n (click)=\"closeModal()\"\n >\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"(fileSelected$ | async) === false || obs.importing\"\n [attr.data-test-id]=\"testIds.submitButton\"\n (click)=\"uploadProcessBpmn()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"obs.activeStep === UPLOAD_STEP.REVIEW\">\n <button\n cdsButton=\"secondary\"\n [attr.data-test-id]=\"testIds.backButton\"\n (click)=\"onBackClick()\"\n >\n {{ 'interface.back' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"obs.preview?.canImport === false || obs.importing\"\n [attr.data-test-id]=\"testIds.importButton\"\n (click)=\"importProcessPackage()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </ng-container>\n\n <button\n *ngIf=\"obs.activeStep === UPLOAD_STEP.SUMMARY\"\n cdsButton=\"primary\"\n [attr.data-test-id]=\"testIds.closeButton\"\n (click)=\"closeModal()\"\n >\n {{ 'interface.close' | translate }}\n </button>\n </cds-modal-footer>\n </ng-container>\n</cds-modal>\n\n<valtimo-render-in-body>\n <valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"processManagement.upload.replaceConfirm\"\n confirmButtonType=\"danger\"\n cancelButtonTextTranslationKey=\"interface.cancel\"\n [content]=\"replaceModalContent\"\n [showModalSubject$]=\"showReplaceConfirmationModal$\"\n titleTranslationKey=\"processManagement.upload.replaceTitle\"\n (confirmEvent)=\"confirmReplace()\"\n (cancelEvent)=\"clearReplaceModal()\"\n ></valtimo-confirmation-modal>\n</valtimo-render-in-body>\n", styles: [".process-management-upload__review{display:flex;flex-direction:column;gap:16px}.process-management-upload__description{font-size:.875rem;line-height:1.25rem;color:var(--cds-text-secondary)}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i6.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i6.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i9.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: FileUploaderModule }, { kind: "component", type: i2$2.FileUploader, selector: "cds-file-uploader, ibm-file-uploader", inputs: ["buttonText", "buttonType", "title", "description", "accept", "multiple", "skeleton", "size", "fileItemSize", "drop", "dropText", "fileUploaderId", "files", "disabled"], outputs: ["filesChange"] }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type: i2$2.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$2.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$2.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$2.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$2.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "ngmodule", type: LayerModule }, { kind: "directive", type: i2$2.LayerDirective, selector: "[cdsLayer], [ibmLayer]", inputs: ["ibmLayer", "cdsLayer"], exportAs: ["layer"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$2.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: ConfirmationModalModule }, { kind: "component", type: i1$1.ConfirmationModalComponent, selector: "valtimo-confirmation-modal", inputs: ["titleTranslationKey", "title", "content", "contentTranslationKey", "confirmButtonText", "confirmButtonTextTranslationKey", "confirmButtonType", "showOptionalButton", "optionalButtonText", "optionalButtonTextTranslationKey", "optionalButtonType", "cancelButtonText", "cancelButtonTextTranslationKey", "cancelButtonType", "showModalSubject$", "outputOnConfirm", "outputOnOptional", "spacerAfterCancelButton"], outputs: ["confirmEvent", "optionalEvent", "cancelEvent"] }, { kind: "component", type: RenderInBodyComponent, selector: "valtimo-render-in-body" }, { kind: "directive", type: ValtimoCdsModalDirective, selector: "[valtimoCdsModal]", inputs: ["minContentHeight"] }, { kind: "ngmodule", type: NotificationModule }, { kind: "component", type: i2$2.Notification, selector: "cds-notification, cds-inline-notification, ibm-notification, ibm-inline-notification", inputs: ["notificationObj"] }, { kind: "component", type: PluginConfigurationMappingComponent, selector: "valtimo-plugin-configuration-mapping", inputs: ["pluginConfigurations"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2249
3654
  }
2250
3655
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ProcessManagementUploadComponent, decorators: [{
2251
3656
  type: Component,
@@ -2260,8 +3665,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2260
3665
  ConfirmationModalModule,
2261
3666
  RenderInBodyComponent,
2262
3667
  ValtimoCdsModalDirective,
2263
- ], template: "<!--\n ~ Copyright 2015-2025 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal valtimoCdsModal [open]=\"modalOpen$ | async\" size=\"sm\" (close)=\"closeModal()\">\n <cds-modal-header showCloseButton=\"true\" (closeSelect)=\"closeModal()\">\n <h3 cdsModalHeaderHeading>{{ 'processManagement.upload.modalTitle' | translate }}</h3>\n </cds-modal-header>\n\n <section cdsModalContent [cdsLayer]=\"1\" [formGroup]=\"form\">\n <cds-file-uploader\n [accept]=\"ACCEPTED_FILES\"\n [buttonText]=\"'processManagement.upload.buttonText' | translate\"\n [description]=\"'processManagement.upload.description' | translate\"\n [multiple]=\"false\"\n [title]=\"'processManagement.upload.title' | translate\"\n buttonType=\"primary\"\n formControlName=\"file\"\n >\n </cds-file-uploader>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"closeModal()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"(fileSelected$ | async) === false\"\n (click)=\"uploadProcessBpmn()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n\n<valtimo-render-in-body>\n <valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"processManagement.upload.replaceConfirm\"\n confirmButtonType=\"danger\"\n cancelButtonTextTranslationKey=\"interface.cancel\"\n [content]=\"replaceModalContent\"\n [showModalSubject$]=\"showReplaceConfirmationModal$\"\n titleTranslationKey=\"processManagement.upload.replaceTitle\"\n (confirmEvent)=\"confirmReplace()\"\n (cancelEvent)=\"clearReplaceModal()\"\n ></valtimo-confirmation-modal>\n</valtimo-render-in-body>\n", styles: ["/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
2264
- }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: i2$1.GlobalNotificationService }, { type: ProcessManagementService }, { type: ProcessManagementStateService }, { type: i5.ProcessLinkService }, { type: i9.TranslateService }] });
3668
+ NotificationModule,
3669
+ PluginConfigurationMappingComponent,
3670
+ ], template: "<!--\n ~ Copyright 2015-2026 Ritense BV, the Netherlands.\n ~\n ~ Licensed under EUPL, Version 1.2 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" basis,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n\n<cds-modal valtimoCdsModal [open]=\"modalOpen$ | async\" size=\"sm\" (close)=\"closeModal()\">\n <cds-modal-header showCloseButton=\"true\" (closeSelect)=\"closeModal()\">\n <h3 cdsModalHeaderHeading>{{ 'processManagement.upload.modalTitle' | translate }}</h3>\n </cds-modal-header>\n\n <ng-container\n *ngIf=\"{\n activeStep: activeStep$ | async,\n preview: preview$ | async,\n missingReferences: missingReferences$ | async,\n importing: importing$ | async,\n } as obs\"\n >\n <section cdsModalContent [cdsLayer]=\"1\" [formGroup]=\"form\" [ngSwitch]=\"obs.activeStep\">\n <cds-file-uploader\n *ngSwitchCase=\"UPLOAD_STEP.FILE_SELECT\"\n [accept]=\"ACCEPTED_FILES\"\n [buttonText]=\"'processManagement.upload.buttonText' | translate\"\n [description]=\"'processManagement.upload.description' | translate\"\n [multiple]=\"false\"\n [title]=\"'processManagement.upload.title' | translate\"\n buttonType=\"primary\"\n formControlName=\"file\"\n [attr.data-test-id]=\"testIds.fileUploader\"\n >\n </cds-file-uploader>\n\n <div *ngSwitchCase=\"UPLOAD_STEP.REVIEW\" class=\"process-management-upload__review\">\n <ng-container *ngFor=\"let group of getMissingReferenceGroups(obs.missingReferences)\">\n <cds-inline-notification\n [notificationObj]=\"{\n type:\n group.type === 'SUB_PROCESS' || group.type === 'DECISION_DEFINITION'\n ? 'warning'\n : 'error',\n title:\n 'processManagement.upload.missingReference.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.missingReference.' + group.type + '.message'\n | translate) +\n ' ' +\n group.references.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n\n <ng-container\n *ngFor=\"let group of getReplacedElementGroups(obs.preview?.elementsToReplace ?? [])\"\n >\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title: 'processManagement.upload.willBeReplaced.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.willBeReplaced.' + group.type + '.message' | translate) +\n ' ' +\n group.keys.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n\n <ng-container *ngIf=\"obs.preview?.pluginConfigurations?.length\">\n <p class=\"process-management-upload__description\">\n {{ 'processManagement.upload.pluginsDescription' | translate }}\n </p>\n\n <valtimo-plugin-configuration-mapping\n [pluginConfigurations]=\"obs.preview.pluginConfigurations\"\n ></valtimo-plugin-configuration-mapping>\n </ng-container>\n </div>\n\n <div *ngSwitchCase=\"UPLOAD_STEP.SUMMARY\" class=\"process-management-upload__review\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'success',\n title: 'processManagement.upload.success' | translate,\n message: 'processManagement.upload.summaryDescription' | translate,\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n\n <ng-container *ngFor=\"let group of getMissingReferenceGroups(obs.missingReferences)\">\n <cds-inline-notification\n [notificationObj]=\"{\n type: 'warning',\n title:\n 'processManagement.upload.missingReference.' + group.type + '.title' | translate,\n message:\n ('processManagement.upload.missingReference.' + group.type + '.message'\n | translate) +\n ' ' +\n group.references.join(', '),\n showClose: false,\n lowContrast: true,\n }\"\n ></cds-inline-notification>\n </ng-container>\n </div>\n </section>\n\n <cds-modal-footer>\n <ng-container *ngIf=\"obs.activeStep === UPLOAD_STEP.FILE_SELECT\">\n <button\n cdsButton=\"secondary\"\n [attr.data-test-id]=\"testIds.cancelButton\"\n (click)=\"closeModal()\"\n >\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"(fileSelected$ | async) === false || obs.importing\"\n [attr.data-test-id]=\"testIds.submitButton\"\n (click)=\"uploadProcessBpmn()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"obs.activeStep === UPLOAD_STEP.REVIEW\">\n <button\n cdsButton=\"secondary\"\n [attr.data-test-id]=\"testIds.backButton\"\n (click)=\"onBackClick()\"\n >\n {{ 'interface.back' | translate }}\n </button>\n\n <button\n cdsButton=\"primary\"\n [disabled]=\"obs.preview?.canImport === false || obs.importing\"\n [attr.data-test-id]=\"testIds.importButton\"\n (click)=\"importProcessPackage()\"\n >\n {{ 'interface.upload' | translate }}\n </button>\n </ng-container>\n\n <button\n *ngIf=\"obs.activeStep === UPLOAD_STEP.SUMMARY\"\n cdsButton=\"primary\"\n [attr.data-test-id]=\"testIds.closeButton\"\n (click)=\"closeModal()\"\n >\n {{ 'interface.close' | translate }}\n </button>\n </cds-modal-footer>\n </ng-container>\n</cds-modal>\n\n<valtimo-render-in-body>\n <valtimo-confirmation-modal\n confirmButtonTextTranslationKey=\"processManagement.upload.replaceConfirm\"\n confirmButtonType=\"danger\"\n cancelButtonTextTranslationKey=\"interface.cancel\"\n [content]=\"replaceModalContent\"\n [showModalSubject$]=\"showReplaceConfirmationModal$\"\n titleTranslationKey=\"processManagement.upload.replaceTitle\"\n (confirmEvent)=\"confirmReplace()\"\n (cancelEvent)=\"clearReplaceModal()\"\n ></valtimo-confirmation-modal>\n</valtimo-render-in-body>\n", styles: [".process-management-upload__review{display:flex;flex-direction:column;gap:16px}.process-management-upload__description{font-size:.875rem;line-height:1.25rem;color:var(--cds-text-secondary)}\n/*!\n * Copyright 2015-2025 Ritense BV, the Netherlands.\n *\n * Licensed under EUPL, Version 1.2 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"] }]
3671
+ }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: i2.GlobalNotificationService }, { type: ProcessManagementService }, { type: ProcessManagementStateService }, { type: i5.ProcessLinkService }, { type: i9.TranslateService }, { type: i0.ChangeDetectorRef }], propDecorators: { _pluginConfigurationMapping: [{
3672
+ type: ViewChild,
3673
+ args: [PluginConfigurationMappingComponent]
3674
+ }] } });
2265
3675
 
2266
3676
  /*
2267
3677
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -2512,5 +3922,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2512
3922
  * Generated bundle index. Do not edit.
2513
3923
  */
2514
3924
 
2515
- export { PROCESS_MANAGEMENT_BUILDER_TEST_IDS, PROCESS_MANAGEMENT_ENDPOINTS, PROCESS_MANAGEMENT_LIST_TEST_IDS, ProcessManagementBuilderComponent, ProcessManagementComponent, ProcessManagementModule, ProcessManagementStateService };
3925
+ export { PROCESS_LINK_PANEL_TEST_IDS, PROCESS_MANAGEMENT_BUILDER_TEST_IDS, PROCESS_MANAGEMENT_ENDPOINTS, PROCESS_MANAGEMENT_LIST_TEST_IDS, PROCESS_MANAGEMENT_UPLOAD_TEST_IDS, ProcessManagementBuilderComponent, ProcessManagementComponent, ProcessManagementModule, ProcessManagementStateService };
2516
3926
  //# sourceMappingURL=valtimo-process-management.mjs.map