@valtimo/process-link 13.44.0 → 13.45.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.
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, InjectionToken, Optional, Inject, Component, EventEmitter, Output, Input, ViewChild, Directive, ChangeDetectionStrategy, NgModule, ViewContainerRef } from '@angular/core';
3
- import { catchError, of, BehaviorSubject, combineLatest, map, Subject, switchMap, filter, Subscription, forkJoin, startWith, mergeMap, tap, withLatestFrom as withLatestFrom$1, take as take$1, shareReplay, distinctUntilChanged } from 'rxjs';
3
+ import { catchError, of, BehaviorSubject, combineLatest, map, Subject, switchMap, throwError, filter, Subscription, forkJoin, startWith, mergeMap, tap, withLatestFrom as withLatestFrom$1, take as take$1, shareReplay, distinctUntilChanged } from 'rxjs';
4
4
  import * as i2 from '@angular/common/http';
5
5
  import { HttpHeaders, HttpParams } from '@angular/common/http';
6
6
  import * as i1 from '@valtimo/shared';
7
7
  import { BaseApiService, InterceptorSkip, getCaseManagementRouteParams, getBuildingBlockManagementRouteParams, getContextObservable, ROLE_ADMIN } from '@valtimo/shared';
8
- import { map as map$1, take, switchMap as switchMap$1, withLatestFrom, filter as filter$1, catchError as catchError$1, tap as tap$1 } from 'rxjs/operators';
8
+ import { map as map$1, take, takeUntil, catchError as catchError$1, switchMap as switchMap$1, withLatestFrom, filter as filter$1, tap as tap$1 } from 'rxjs/operators';
9
9
  import * as i1$1 from '@valtimo/plugin';
10
10
  import { PluginTranslatePipeModule, PluginConfigurationContainerModule } from '@valtimo/plugin';
11
11
  import * as i1$5 from '@valtimo/form-flow-management';
@@ -545,6 +545,7 @@ class PluginStateService {
545
545
  this._selectedPluginFunction$ = new BehaviorSubject(undefined);
546
546
  this._save$ = new Subject();
547
547
  this._selectedProcessLink$ = new BehaviorSubject(undefined);
548
+ this._cancelPluginLoad$ = new Subject();
548
549
  }
549
550
  get selectedPluginDefinition$() {
550
551
  return this._selectedPluginDefinition$.asObservable();
@@ -568,20 +569,7 @@ class PluginStateService {
568
569
  this._selectedPluginConfiguration$,
569
570
  this._selectedPluginDefinition$,
570
571
  ]).pipe(map$1(([configuration, definition]) => configuration?.pluginDefinition.key || definition?.key))
571
- : combineLatest([
572
- this._selectedProcessLink$,
573
- this.pluginService.pluginSpecifications$,
574
- ]).pipe(map$1(([processLink, pluginSpecifications]) => {
575
- if (processLink?.pluginDefinitionKey) {
576
- return processLink.pluginDefinitionKey;
577
- }
578
- const pluginSpecification = pluginSpecifications.find(specification => {
579
- const functionKeys = specification?.functionConfigurationComponents &&
580
- Object.keys(specification.functionConfigurationComponents);
581
- return functionKeys?.includes(processLink?.pluginActionDefinitionKey);
582
- });
583
- return pluginSpecification?.pluginId;
584
- }))));
572
+ : this.getPluginDefinitionKeyForProcessLink(selectedProcesLink)));
585
573
  }
586
574
  selectPluginDefinition(definition) {
587
575
  this._selectedPluginDefinition$.next(definition);
@@ -593,62 +581,83 @@ class PluginStateService {
593
581
  this._selectedPluginFunction$.next(pluginFunction);
594
582
  }
595
583
  selectProcessLink(processLink) {
584
+ this._cancelPluginLoad$.next();
585
+ this.clearPluginSelection();
596
586
  this._selectedProcessLink$.next(processLink);
597
587
  // When editing a plugin process link, populate the plugin definition
598
588
  if (processLink?.processLinkType === 'plugin') {
599
589
  this.loadPluginDefinitionForProcessLink(processLink);
600
590
  }
601
591
  }
592
+ clearPluginSelection() {
593
+ this._selectedPluginDefinition$.next(undefined);
594
+ this._selectedPluginConfiguration$.next(undefined);
595
+ this._selectedPluginFunction$.next(undefined);
596
+ }
602
597
  loadPluginDefinitionForProcessLink(processLink) {
603
- // Get the plugin definition key - either directly or from plugin specifications
598
+ // Get the plugin definition key - either directly or from the configuration the link points at
604
599
  this.getPluginDefinitionKeyForProcessLink(processLink)
605
- .pipe(take(1))
606
- .subscribe(pluginDefinitionKey => {
607
- if (pluginDefinitionKey) {
608
- // Fetch all plugin definitions and find the one matching the key
609
- this.pluginManagementService
610
- .getPluginDefinitions()
611
- .pipe(take(1), map$1(definitions => definitions.find(d => d.key === pluginDefinitionKey)))
612
- .subscribe(definition => {
613
- if (definition) {
614
- this._selectedPluginDefinition$.next(definition);
615
- // Also set the selected function if available
616
- if (processLink.pluginActionDefinitionKey) {
617
- this._selectedPluginFunction$.next({
618
- key: processLink.pluginActionDefinitionKey,
619
- });
600
+ .pipe(take(1), takeUntil(this._cancelPluginLoad$))
601
+ .subscribe({
602
+ next: pluginDefinitionKey => {
603
+ if (pluginDefinitionKey) {
604
+ // Fetch all plugin definitions and find the one matching the key
605
+ this.pluginManagementService
606
+ .getPluginDefinitions()
607
+ .pipe(take(1), map$1(definitions => definitions.find(d => d.key === pluginDefinitionKey)), takeUntil(this._cancelPluginLoad$))
608
+ .subscribe(definition => {
609
+ if (definition) {
610
+ this._selectedPluginDefinition$.next(definition);
611
+ // Also set the selected function if available
612
+ if (processLink.pluginActionDefinitionKey) {
613
+ this._selectedPluginFunction$.next({
614
+ key: processLink.pluginActionDefinitionKey,
615
+ });
616
+ }
620
617
  }
621
- }
622
- });
623
- }
618
+ });
619
+ }
620
+ },
621
+ error: () => this.clearPluginSelection(),
624
622
  });
625
623
  // Load and set the plugin configuration if available
626
624
  if (processLink.pluginConfigurationId) {
627
- this.pluginManagementService
628
- .getAllPluginConfigurations()
629
- .pipe(take(1), map$1(configs => configs.find(c => c.id === processLink.pluginConfigurationId)))
630
- .subscribe(configuration => {
631
- if (configuration) {
632
- this._selectedPluginConfiguration$.next(configuration);
633
- }
625
+ this.getPluginConfigurationForProcessLink(processLink)
626
+ .pipe(take(1), takeUntil(this._cancelPluginLoad$))
627
+ .subscribe({
628
+ next: configuration => {
629
+ if (configuration) {
630
+ this._selectedPluginConfiguration$.next(configuration);
631
+ }
632
+ },
633
+ error: () => this.clearPluginSelection(),
634
634
  });
635
635
  }
636
636
  }
637
637
  getPluginDefinitionKeyForProcessLink(processLink) {
638
638
  // If the key is directly available, use it
639
- if (processLink.pluginDefinitionKey) {
639
+ if (processLink?.pluginDefinitionKey) {
640
640
  return of(processLink.pluginDefinitionKey);
641
641
  }
642
- // Otherwise, derive it from plugin specifications using the action key
642
+ // An action key can occur in several plugins, so the configuration the link points at decides which one
643
+ if (processLink?.pluginConfigurationId) {
644
+ return this.getPluginConfigurationForProcessLink(processLink).pipe(map$1(configuration => configuration?.pluginDefinition?.key));
645
+ }
646
+ // Only a link recording neither is left to the action key, where a single match is all there is to go on
643
647
  return this.pluginService.pluginSpecifications$.pipe(map$1(pluginSpecifications => {
644
648
  const pluginSpecification = pluginSpecifications.find(specification => {
645
649
  const functionKeys = specification?.functionConfigurationComponents &&
646
650
  Object.keys(specification.functionConfigurationComponents);
647
- return functionKeys?.includes(processLink.pluginActionDefinitionKey);
651
+ return functionKeys?.includes(processLink?.pluginActionDefinitionKey);
648
652
  });
649
653
  return pluginSpecification?.pluginId;
650
654
  }));
651
655
  }
656
+ getPluginConfigurationForProcessLink(processLink) {
657
+ return this.pluginManagementService
658
+ .getPluginConfiguration(processLink.pluginConfigurationId)
659
+ .pipe(catchError$1(error => (error?.status === 404 ? of(undefined) : throwError(() => error))));
660
+ }
652
661
  deselectProcessLink() {
653
662
  this._selectedProcessLink$.next(undefined);
654
663
  }
@@ -2081,11 +2090,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2081
2090
  * limitations under the License.
2082
2091
  */
2083
2092
  class SelectPluginActionComponent {
2084
- constructor(buttonService, injector, pluginManagementService, pluginService, stateService, stepService, processLinkStateService) {
2093
+ constructor(buttonService, injector, pluginManagementService, pluginService, pluginTranslationService, stateService, stepService, processLinkStateService) {
2085
2094
  this.buttonService = buttonService;
2086
2095
  this.injector = injector;
2087
2096
  this.pluginManagementService = pluginManagementService;
2088
2097
  this.pluginService = pluginService;
2098
+ this.pluginTranslationService = pluginTranslationService;
2089
2099
  this.stateService = stateService;
2090
2100
  this.stepService = stepService;
2091
2101
  this.processLinkStateService = processLinkStateService;
@@ -2108,7 +2118,7 @@ class SelectPluginActionComponent {
2108
2118
  const props = (selectedConfiguration?.properties ?? {});
2109
2119
  const filterFn = specification.functionConfigurationComponentsFilter;
2110
2120
  return forkJoin(functions.map(fn => filterFn(props, fn.key, this.injector).pipe(map$1(visible => ({ fn, visible }))))).pipe(map$1(results => results.filter(r => r.visible).map(r => r.fn)));
2111
- }));
2121
+ }), switchMap$1(functions => this.sortByTranslatedTitle(functions, selectedDefinition.key)));
2112
2122
  }));
2113
2123
  this.selectedPluginDefinition$ = this.stateService.selectedPluginDefinition$;
2114
2124
  this.selectedPluginFunction$ = this.stateService.selectedPluginFunction$;
@@ -2131,6 +2141,15 @@ class SelectPluginActionComponent {
2131
2141
  stringify(object) {
2132
2142
  return JSON.stringify(object);
2133
2143
  }
2144
+ sortByTranslatedTitle(pluginFunctions, pluginDefinitionKey) {
2145
+ if (!pluginFunctions?.length)
2146
+ return of(pluginFunctions);
2147
+ return combineLatest(pluginFunctions.map(pluginFunction => this.pluginTranslationService
2148
+ .translate(pluginFunction.key, pluginDefinitionKey)
2149
+ .pipe(map$1(title => ({ pluginFunction, title }))))).pipe(map$1(translatedFunctions => [...translatedFunctions]
2150
+ .sort((a, b) => a.title.localeCompare(b.title))
2151
+ .map(translatedFunction => translatedFunction.pluginFunction)));
2152
+ }
2134
2153
  openBackButtonSubscription() {
2135
2154
  this.buttonService.backButtonClick$
2136
2155
  .pipe(withLatestFrom(this.processLinkStateService.isEditing$), filter$1(([, isEditing]) => !isEditing), switchMap$1(() => this.stepService.hasOneProcessLinkType$), take(1))
@@ -2145,13 +2164,13 @@ class SelectPluginActionComponent {
2145
2164
  this.stepService.setConfigurePluginActionSteps();
2146
2165
  }));
2147
2166
  }
2148
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SelectPluginActionComponent, deps: [{ token: ProcessLinkButtonService }, { token: i0.Injector }, { token: i1$1.PluginManagementService }, { token: i1$1.PluginService }, { token: PluginStateService }, { token: ProcessLinkStepService }, { token: ProcessLinkStateService }], target: i0.ɵɵFactoryTarget.Component }); }
2167
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SelectPluginActionComponent, deps: [{ token: ProcessLinkButtonService }, { token: i0.Injector }, { token: i1$1.PluginManagementService }, { token: i1$1.PluginService }, { token: i1$1.PluginTranslationService }, { token: PluginStateService }, { token: ProcessLinkStepService }, { token: ProcessLinkStateService }], target: i0.ɵɵFactoryTarget.Component }); }
2149
2168
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: SelectPluginActionComponent, isStandalone: false, selector: "valtimo-select-plugin-action", 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<ng-container *ngIf=\"pluginFunctions$ | async as pluginFunctions; else loading\">\n <div\n class=\"choose-plugin-action\"\n *ngIf=\"pluginFunctions?.length > 0\"\n [attr.data-test-id]=\"testIds.container\"\n >\n <p class=\"step-description\">\n {{ 'processLinkConfiguration.choosePluginActionDescription' | translate }}\n </p>\n <div class=\"action-tiles\">\n <cds-tile-group\n [attr.data-test-id]=\"testIds.tileGroup\"\n [multiple]=\"false\"\n *ngIf=\"{selectedPluginDefinition: selectedPluginDefinition$ | async} as obs\"\n (selected)=\"selected($event)\"\n >\n <cds-selection-tile\n *ngFor=\"let pluginFunction of pluginFunctions\"\n [attr.data-test-id]=\"tileTestIdPrefix + pluginFunction.key\"\n [value]=\"stringify(pluginFunction)\"\n [selected]=\"false\"\n >{{\n pluginFunction.key | pluginTranslate: obs.selectedPluginDefinition.key | async\n }}</cds-selection-tile\n >\n </cds-tile-group>\n </div>\n </div>\n <ng-container *ngIf=\"!pluginFunctions || pluginFunctions?.length === 0\">\n <ng-container *ngTemplateOutlet=\"noPluginFunctionsTemplate\"> </ng-container>\n </ng-container>\n</ng-container>\n\n<ng-template #noPluginFunctionsTemplate>\n <p class=\"step-description\" [attr.data-test-id]=\"testIds.noActionsMessage\">\n {{ 'processLink.noPluginFunctions' | translate }}\n </p>\n</ng-template>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".btn-back{position:absolute}.cards-container{display:flex;flex-flow:row wrap;width:100%}.card{width:calc(33.3% - 20px);margin-right:30px}.card:nth-child(3n+3){margin-right:0}.card-header{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.loading-container{display:flex;justify-content:center;margin-top:40px;flex-direction:row}.action-tiles{margin-top:24px;width:calc(100% + 8px);margin-left:-8px}::ng-deep .choose-plugin-action cds-selection-tile{display:flex;width:calc(33.3% - 8px)}::ng-deep .choose-plugin-action .cds--tile{width:100%}::ng-deep .choose-plugin-action cds-tile-group fieldset{display:flex;flex-direction:row;flex-wrap:wrap}::ng-deep .choose-plugin-action cds-selection-tile{margin-left:8px}::ng-deep .choose-plugin-action cds-selection-tile:nth-child(n+4){margin-top:8px}::ng-deep .action-tiles .cds--tile{background-color:var(--cds-layer-02, #ffffff)}::ng-deep .action-tiles .cds--tile:hover{background:var(--cds-layer-hover)}\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: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i5.SelectionTile, selector: "cds-selection-tile, ibm-selection-tile", inputs: ["theme", "id", "selected", "value", "disabled"], outputs: ["change"] }, { kind: "component", type: i5.TileGroup, selector: "cds-tile-group, ibm-tile-group", inputs: ["name", "multiple", "legend"], outputs: ["selected"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "pipe", type: i1$1.PluginTranslatePipe, name: "pluginTranslate" }] }); }
2150
2169
  }
2151
2170
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SelectPluginActionComponent, decorators: [{
2152
2171
  type: Component,
2153
2172
  args: [{ standalone: false, selector: 'valtimo-select-plugin-action', 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<ng-container *ngIf=\"pluginFunctions$ | async as pluginFunctions; else loading\">\n <div\n class=\"choose-plugin-action\"\n *ngIf=\"pluginFunctions?.length > 0\"\n [attr.data-test-id]=\"testIds.container\"\n >\n <p class=\"step-description\">\n {{ 'processLinkConfiguration.choosePluginActionDescription' | translate }}\n </p>\n <div class=\"action-tiles\">\n <cds-tile-group\n [attr.data-test-id]=\"testIds.tileGroup\"\n [multiple]=\"false\"\n *ngIf=\"{selectedPluginDefinition: selectedPluginDefinition$ | async} as obs\"\n (selected)=\"selected($event)\"\n >\n <cds-selection-tile\n *ngFor=\"let pluginFunction of pluginFunctions\"\n [attr.data-test-id]=\"tileTestIdPrefix + pluginFunction.key\"\n [value]=\"stringify(pluginFunction)\"\n [selected]=\"false\"\n >{{\n pluginFunction.key | pluginTranslate: obs.selectedPluginDefinition.key | async\n }}</cds-selection-tile\n >\n </cds-tile-group>\n </div>\n </div>\n <ng-container *ngIf=\"!pluginFunctions || pluginFunctions?.length === 0\">\n <ng-container *ngTemplateOutlet=\"noPluginFunctionsTemplate\"> </ng-container>\n </ng-container>\n</ng-container>\n\n<ng-template #noPluginFunctionsTemplate>\n <p class=\"step-description\" [attr.data-test-id]=\"testIds.noActionsMessage\">\n {{ 'processLink.noPluginFunctions' | translate }}\n </p>\n</ng-template>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".btn-back{position:absolute}.cards-container{display:flex;flex-flow:row wrap;width:100%}.card{width:calc(33.3% - 20px);margin-right:30px}.card:nth-child(3n+3){margin-right:0}.card-header{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.loading-container{display:flex;justify-content:center;margin-top:40px;flex-direction:row}.action-tiles{margin-top:24px;width:calc(100% + 8px);margin-left:-8px}::ng-deep .choose-plugin-action cds-selection-tile{display:flex;width:calc(33.3% - 8px)}::ng-deep .choose-plugin-action .cds--tile{width:100%}::ng-deep .choose-plugin-action cds-tile-group fieldset{display:flex;flex-direction:row;flex-wrap:wrap}::ng-deep .choose-plugin-action cds-selection-tile{margin-left:8px}::ng-deep .choose-plugin-action cds-selection-tile:nth-child(n+4){margin-top:8px}::ng-deep .action-tiles .cds--tile{background-color:var(--cds-layer-02, #ffffff)}::ng-deep .action-tiles .cds--tile:hover{background:var(--cds-layer-hover)}\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"] }]
2154
- }], ctorParameters: () => [{ type: ProcessLinkButtonService }, { type: i0.Injector }, { type: i1$1.PluginManagementService }, { type: i1$1.PluginService }, { type: PluginStateService }, { type: ProcessLinkStepService }, { type: ProcessLinkStateService }] });
2173
+ }], ctorParameters: () => [{ type: ProcessLinkButtonService }, { type: i0.Injector }, { type: i1$1.PluginManagementService }, { type: i1$1.PluginService }, { type: i1$1.PluginTranslationService }, { type: PluginStateService }, { type: ProcessLinkStepService }, { type: ProcessLinkStateService }] });
2155
2174
 
2156
2175
  /*
2157
2176
  * Copyright 2015-2025 Ritense BV, the Netherlands.