@valtimo/access-control-management 13.30.0 → 13.32.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,22 +1,76 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, NgModule } from '@angular/core';
3
- import { BehaviorSubject, tap, switchMap, take, catchError, of, combineLatest, map, Subject, finalize, delay, filter } from 'rxjs';
2
+ import { Injectable, EventEmitter, Output, Input, ChangeDetectionStrategy, Component, ViewChild, Pipe, signal, NgModule } from '@angular/core';
3
+ import { BehaviorSubject, tap, switchMap, take, catchError, of, shareReplay, map, combineLatest, Subject, finalize, delay, Subscription, skip, filter } from 'rxjs';
4
4
  import * as i1 from '@valtimo/shared';
5
5
  import { ROLE_ADMIN } from '@valtimo/shared';
6
6
  import * as i2 from '@angular/common/http';
7
7
  import * as i4 from '@angular/common';
8
8
  import { CommonModule } from '@angular/common';
9
- import * as i5 from '@ngx-translate/core';
9
+ import * as i4$1 from '@ngx-translate/core';
10
10
  import { TranslateModule } from '@ngx-translate/core';
11
- import * as i3$1 from '@valtimo/components';
12
- import { CARBON_CONSTANTS, ViewType, CarbonListComponent, ConfirmationModalModule, EditorModule, RenderInPageHeaderDirective, OverflowMenuComponent, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, CarbonListModule } from '@valtimo/components';
13
- import * as i2$1 from '@angular/router';
11
+ import * as i3 from '@valtimo/components';
12
+ import { CARBON_CONSTANTS, ViewType, CarbonListComponent, EditorModule, OverflowMenuComponent, ConfirmationModalModule, RenderInPageHeaderDirective, OverflowMenuOptionComponent, OverflowMenuTriggerComponent, CarbonListModule } from '@valtimo/components';
13
+ import * as i1$2 from '@angular/router';
14
14
  import { RouterModule } from '@angular/router';
15
15
  import { AuthGuardService } from '@valtimo/security';
16
- import * as i3 from 'carbon-components-angular';
17
- import { ButtonModule, ModalModule, InputModule, IconModule, LoadingModule, NotificationModule } from 'carbon-components-angular';
16
+ import * as i2$1 from 'carbon-components-angular';
17
+ import { ButtonModule, DropdownModule, IconModule, ModalModule, InputModule, LoadingModule, NotificationModule, TabsModule } from 'carbon-components-angular';
18
18
  import * as i1$1 from '@angular/forms';
19
19
  import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
20
+ import { Filter16, TrashCan16 } from '@carbon/icons';
21
+
22
+ /*
23
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
24
+ *
25
+ * Licensed under EUPL, Version 1.2 (the "License");
26
+ * you may not use this file except in compliance with the License.
27
+ * You may obtain a copy of the License at
28
+ *
29
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
30
+ *
31
+ * Unless required by applicable law or agreed to in writing, software
32
+ * distributed under the License is distributed on an "AS IS" basis,
33
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34
+ * See the License for the specific language governing permissions and
35
+ * limitations under the License.
36
+ */
37
+ var AccessControlEditorTab;
38
+ (function (AccessControlEditorTab) {
39
+ AccessControlEditorTab["SUMMARY"] = "summary";
40
+ AccessControlEditorTab["JSON_EDITOR"] = "jsonEditor";
41
+ })(AccessControlEditorTab || (AccessControlEditorTab = {}));
42
+
43
+ /*
44
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
45
+ *
46
+ * Licensed under EUPL, Version 1.2 (the "License");
47
+ * you may not use this file except in compliance with the License.
48
+ * You may obtain a copy of the License at
49
+ *
50
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
51
+ *
52
+ * Unless required by applicable law or agreed to in writing, software
53
+ * distributed under the License is distributed on an "AS IS" basis,
54
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
55
+ * See the License for the specific language governing permissions and
56
+ * limitations under the License.
57
+ */
58
+
59
+ /*
60
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
61
+ *
62
+ * Licensed under EUPL, Version 1.2 (the "License");
63
+ * you may not use this file except in compliance with the License.
64
+ * You may obtain a copy of the License at
65
+ *
66
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
67
+ *
68
+ * Unless required by applicable law or agreed to in writing, software
69
+ * distributed under the License is distributed on an "AS IS" basis,
70
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
71
+ * See the License for the specific language governing permissions and
72
+ * limitations under the License.
73
+ */
20
74
 
21
75
  /*
22
76
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -106,6 +160,9 @@ class AccessControlService {
106
160
  updateRolePermissions(roleKey, updatedPermission) {
107
161
  return this.http.put(`${this.valtimoEndpointUri}v1/roles/${roleKey}/permissions`, updatedPermission);
108
162
  }
163
+ getPermissionSchema() {
164
+ return this.http.get(`${this.valtimoEndpointUri}v1/permissions/schema`);
165
+ }
109
166
  updateRole(roleKey, request) {
110
167
  return this.http.put(`${this.valtimoEndpointUri}v1/roles/${roleKey}`, request);
111
168
  }
@@ -117,6 +174,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
117
174
  args: [{ providedIn: 'root' }]
118
175
  }], ctorParameters: () => [{ type: i1.ConfigService }, { type: i2.HttpClient }] });
119
176
 
177
+ /*
178
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
179
+ *
180
+ * Licensed under EUPL, Version 1.2 (the "License");
181
+ * you may not use this file except in compliance with the License.
182
+ * You may obtain a copy of the License at
183
+ *
184
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
185
+ *
186
+ * Unless required by applicable law or agreed to in writing, software
187
+ * distributed under the License is distributed on an "AS IS" basis,
188
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ * See the License for the specific language governing permissions and
190
+ * limitations under the License.
191
+ */
192
+ class PermissionSchemaMetadataService {
193
+ constructor(accessControlService) {
194
+ this.accessControlService = accessControlService;
195
+ this._knownResourceTypes$ = new BehaviorSubject(new Set());
196
+ this._fieldsByResourceType$ = new BehaviorSubject({});
197
+ this.schema$ = this.accessControlService
198
+ .getPermissionSchema()
199
+ .pipe(tap(schema => {
200
+ this._knownResourceTypes$.next(this.extractKnownResourceTypes(schema));
201
+ this._fieldsByResourceType$.next(this.extractFieldsByResourceType(schema));
202
+ }), shareReplay({ bufferSize: 1, refCount: false }));
203
+ this.actionsByResourceType$ = this.schema$.pipe(map(schema => this.extractActionsByResourceType(schema)));
204
+ this.allResourceTypes$ = this.actionsByResourceType$.pipe(map(actions => Object.keys(actions)));
205
+ }
206
+ isResourceTypeKnown(fqn) {
207
+ return this._knownResourceTypes$.value.has(fqn);
208
+ }
209
+ isFieldKnown(resourceType, field) {
210
+ return this._fieldsByResourceType$.value[resourceType]?.has(field) ?? false;
211
+ }
212
+ extractActionsByResourceType(schema) {
213
+ const branches = schema?.items?.allOf ?? [];
214
+ const result = {};
215
+ for (const branch of branches) {
216
+ const resourceType = branch?.if?.properties?.resourceType?.const;
217
+ const actions = branch?.then?.properties?.action?.enum;
218
+ if (resourceType && Array.isArray(actions)) {
219
+ result[resourceType] = [...actions];
220
+ }
221
+ }
222
+ return result;
223
+ }
224
+ extractKnownResourceTypes(schema) {
225
+ const entries = schema?.items?.properties?.resourceType?.oneOf ?? [];
226
+ const result = new Set();
227
+ for (const entry of entries) {
228
+ if (entry?.const)
229
+ result.add(entry.const);
230
+ }
231
+ return result;
232
+ }
233
+ extractFieldsByResourceType(schema) {
234
+ const definitions = schema?.definitions ?? {};
235
+ const result = {};
236
+ for (const [key, def] of Object.entries(definitions)) {
237
+ if (!key.startsWith('condList.'))
238
+ continue;
239
+ const resourceType = key.substring('condList.'.length);
240
+ const fields = new Set();
241
+ for (const variant of def?.items?.oneOf ?? []) {
242
+ for (const part of variant?.allOf ?? []) {
243
+ for (const f of part?.properties?.field?.enum ?? [])
244
+ fields.add(f);
245
+ }
246
+ }
247
+ result[resourceType] = fields;
248
+ }
249
+ return result;
250
+ }
251
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, deps: [{ token: AccessControlService }], target: i0.ɵɵFactoryTarget.Injectable }); }
252
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, providedIn: 'root' }); }
253
+ }
254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: PermissionSchemaMetadataService, decorators: [{
255
+ type: Injectable,
256
+ args: [{ providedIn: 'root' }]
257
+ }], ctorParameters: () => [{ type: AccessControlService }] });
258
+
120
259
  /*
121
260
  * Copyright 2015-2025 Ritense BV, the Netherlands.
122
261
  *
@@ -237,7 +376,7 @@ class RoleMetadataModalComponent {
237
376
  }, CARBON_CONSTANTS.modalAnimationMs);
238
377
  }
239
378
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RoleMetadataModalComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
240
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: RoleMetadataModalComponent, isStandalone: false, selector: "valtimo-role-metadata-modal", inputs: { open: "open", type: "type", defaultKeyValue: "defaultKeyValue" }, outputs: { closeEvent: "closeEvent" }, 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\n *ngIf=\"{\n isAdd: type === 'add',\n } as vars\"\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{\n vars.isAdd\n ? ('accessControl.roles.add' | translate)\n : ('accessControl.roles.editRole' | translate)\n }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'accessControl.roles.name' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'accessControl.roles.name' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ vars.isAdd ? ('interface.create' | translate) : ('interface.save' | translate) }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i3.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i3.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i3.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i3.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i3.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i3.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "pipe", type: i5.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
379
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: RoleMetadataModalComponent, isStandalone: false, selector: "valtimo-role-metadata-modal", inputs: { open: "open", type: "type", defaultKeyValue: "defaultKeyValue" }, outputs: { closeEvent: "closeEvent" }, 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\n *ngIf=\"{\n isAdd: type === 'add',\n } as vars\"\n [open]=\"open\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n valtimoCdsModal\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{\n vars.isAdd\n ? ('accessControl.roles.add' | translate)\n : ('accessControl.roles.editRole' | translate)\n }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <form [formGroup]=\"form\">\n <cds-label [invalid]=\"key.dirty && key.invalid\">\n {{ 'accessControl.roles.name' | translate }}\n\n <input\n formControlName=\"key\"\n cdsText\n placeholder=\"{{ 'accessControl.roles.name' | translate }}\"\n [attr.modal-primary-focus]=\"true\"\n [invalid]=\"key.dirty && key.invalid\"\n />\n </cds-label>\n </form>\n </section>\n\n <cds-modal-footer>\n <button cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button [disabled]=\"this.form.invalid\" (click)=\"onConfirm()\" cdsButton=\"primary\">\n {{ vars.isAdd ? ('interface.create' | translate) : ('interface.save' | translate) }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.Label, selector: "cds-label, ibm-label", inputs: ["labelInputID", "disabled", "skeleton", "helperText", "invalidText", "invalid", "warn", "warnText", "ariaLabel"] }, { kind: "directive", type: i2$1.TextInput, selector: "[cdsText], [ibmText]", inputs: ["theme", "size", "invalid", "warn", "skeleton"] }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
241
380
  }
242
381
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RoleMetadataModalComponent, decorators: [{
243
382
  type: Component,
@@ -275,7 +414,7 @@ class DeleteRoleModalComponent {
275
414
  this.deleteEvent.emit(roles);
276
415
  }
277
416
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
278
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DeleteRoleModalComponent, isStandalone: false, selector: "valtimo-delete-role-modal", inputs: { deleteRowKeys: "deleteRowKeys", showDeleteModal$: "showDeleteModal$" }, outputs: { deleteEvent: "deleteEvent" }, 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-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"accessControl.roles.deleteModalContent\"\n [outputOnConfirm]=\"deleteRowKeys\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "component", type: i3$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 }); }
417
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: DeleteRoleModalComponent, isStandalone: false, selector: "valtimo-delete-role-modal", inputs: { deleteRowKeys: "deleteRowKeys", showDeleteModal$: "showDeleteModal$" }, outputs: { deleteEvent: "deleteEvent" }, 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-confirmation-modal\n confirmButtonTextTranslationKey=\"interface.delete\"\n confirmButtonType=\"danger\"\n contentTranslationKey=\"accessControl.roles.deleteModalContent\"\n [outputOnConfirm]=\"deleteRowKeys\"\n [showModalSubject$]=\"showDeleteModal$\"\n titleTranslationKey=\"interface.delete\"\n (confirmEvent)=\"onDelete($event)\"\n></valtimo-confirmation-modal>\n", dependencies: [{ kind: "component", type: i3.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 }); }
279
418
  }
280
419
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, decorators: [{
281
420
  type: Component,
@@ -340,7 +479,7 @@ class ExportRoleModalComponent {
340
479
  });
341
480
  }
342
481
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
343
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ExportRoleModalComponent, isStandalone: false, selector: "valtimo-export-role-modal", inputs: { open: "open", exportRowKeys: "exportRowKeys", reset$: "reset$", disabled: "disabled" }, outputs: { exportEvent: "exportEvent", closeEvent: "closeEvent" }, 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\n *ngIf=\"{title: ('interface.export' | translate), selectedType: selectedType$ | async} as vars\"\n valtimoCdsModal\n [open]=\"open\"\n size=\"sm\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ vars.title }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <div class=\"export-buttons\">\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('unified')\">\n {{ 'accessControl.roles.singleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"document\" size=\"16\"></svg>\n </button>\n\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('separate')\">\n {{ 'accessControl.roles.multipleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"copy--file\" size=\"16\"></svg>\n </button>\n </div>\n </section>\n\n <cds-modal-footer>\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n [disabled]=\"!vars.selectedType || disabled\"\n cdsButton=\"primary\"\n (click)=\"onConfirm(vars.selectedType)\"\n >\n {{ vars.title }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".export-buttons{display:grid;width:100%;grid-template-columns:1fr 1fr;column-gap:16px}.export-buttons button{width:100%}\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.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "component", type: i3.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i3.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i3.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i3.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i3.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
482
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: ExportRoleModalComponent, isStandalone: false, selector: "valtimo-export-role-modal", inputs: { open: "open", exportRowKeys: "exportRowKeys", reset$: "reset$", disabled: "disabled" }, outputs: { exportEvent: "exportEvent", closeEvent: "closeEvent" }, 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\n *ngIf=\"{title: ('interface.export' | translate), selectedType: selectedType$ | async} as vars\"\n valtimoCdsModal\n [open]=\"open\"\n size=\"sm\"\n showFooter=\"true\"\n [title]=\"vars.title\"\n (close)=\"onCancel()\"\n>\n <cds-modal-header [showCloseButton]=\"true\" (closeSelect)=\"onCancel()\">\n <h3 cdsModalHeaderHeading>\n {{ vars.title }}\n </h3>\n </cds-modal-header>\n\n <section cdsModalContent>\n <div class=\"export-buttons\">\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('unified')\">\n {{ 'accessControl.roles.singleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"document\" size=\"16\"></svg>\n </button>\n\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"selectType('separate')\">\n {{ 'accessControl.roles.multipleExport' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"copy--file\" size=\"16\"></svg>\n </button>\n </div>\n </section>\n\n <cds-modal-footer>\n <button [disabled]=\"disabled\" cdsButton=\"secondary\" (click)=\"onCancel()\">\n {{ 'interface.cancel' | translate }}\n </button>\n\n <button\n [disabled]=\"!vars.selectedType || disabled\"\n cdsButton=\"primary\"\n (click)=\"onConfirm(vars.selectedType)\"\n >\n {{ vars.title }}\n </button>\n </cds-modal-footer>\n</cds-modal>\n", styles: [".export-buttons{display:grid;width:100%;grid-template-columns:1fr 1fr;column-gap:16px}.export-buttons button{width:100%}\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.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "component", type: i2$1.Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: i2$1.ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: i2$1.ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: i2$1.ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: i2$1.ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
344
483
  }
345
484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, decorators: [{
346
485
  type: Component,
@@ -451,17 +590,494 @@ class AccessControlOverviewComponent {
451
590
  setSelectedRoleKeys() {
452
591
  this.selectedRowKeys$.next(this.carbonList.selectedItems.map((role) => role.roleKey));
453
592
  }
454
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, deps: [{ token: AccessControlService }, { token: AccessControlExportService }, { token: i2$1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
455
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], 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 [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i3$1.CarbonListComponent, selector: "valtimo-carbon-list", inputs: ["items", "fields", "tableTranslations", "paginatorConfig", "pagination", "loading", "skeletonRowCount", "actions", "actionItems", "showActionItems", "header", "hideColumnHeader", "initialSortState", "sortState", "isSearchable", "enableSingleSelection", "lastColumnTemplate", "paginationIdentifier", "showSelectionColumn", "striped", "hideToolbar", "lockedTooltipTranslationKey", "movingRowsEnabled", "dragAndDrop", "dragAndDropDisabled"], outputs: ["rowClicked", "paginationClicked", "paginationSet", "search", "sortChanged", "moveRow", "itemsReordered"] }, { kind: "component", type: i3$1.CarbonNoResultsComponent, selector: "valtimo-no-results", inputs: ["action", "description", "illustration", "title", "smallPadding", "collapseVertically", "alwaysRenderVertically"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "component", type: ExportRoleModalComponent, selector: "valtimo-export-role-modal", inputs: ["open", "exportRowKeys", "reset$", "disabled"], outputs: ["exportEvent", "closeEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
593
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, deps: [{ token: AccessControlService }, { token: AccessControlExportService }, { token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
594
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlOverviewComponent, isStandalone: false, selector: "ng-component", viewQueries: [{ propertyName: "carbonList", first: true, predicate: CarbonListComponent, descendants: true }], 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 [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i3.CarbonListComponent, selector: "valtimo-carbon-list", inputs: ["items", "fields", "tableTranslations", "paginatorConfig", "pagination", "loading", "skeletonRowCount", "actions", "actionItems", "showActionItems", "header", "hideColumnHeader", "initialSortState", "sortState", "isSearchable", "enableSingleSelection", "lastColumnTemplate", "paginationIdentifier", "showSelectionColumn", "striped", "hideToolbar", "lockedTooltipTranslationKey", "movingRowsEnabled", "dragAndDrop", "dragAndDropDisabled"], outputs: ["rowClicked", "paginationClicked", "paginationSet", "search", "sortChanged", "moveRow", "itemsReordered"] }, { kind: "component", type: i3.CarbonNoResultsComponent, selector: "valtimo-no-results", inputs: ["action", "description", "illustration", "title", "smallPadding", "collapseVertically", "alwaysRenderVertically"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "component", type: ExportRoleModalComponent, selector: "valtimo-export-role-modal", inputs: ["open", "exportRowKeys", "reset$", "disabled"], outputs: ["exportEvent", "closeEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
456
595
  }
457
596
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, decorators: [{
458
597
  type: Component,
459
598
  args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, 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 [header]=\"false\"\n [items]=\"roles$ | async\"\n [fields]=\"fields\"\n [loading]=\"loading$ | async\"\n [showSelectionColumn]=\"true\"\n (rowClicked)=\"onRowClick($event)\"\n>\n <ng-container header> {{ 'accessControl.roles.title' | translate }} </ng-container>\n\n <div carbonToolbarContent>\n <ng-container [ngTemplateOutlet]=\"addRoleButton\"></ng-container>\n </div>\n\n <ng-container carbonToolbarActions>\n <button cdsButton=\"primary\" (click)=\"showDeleteModal()\">\n {{ 'interface.delete' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n\n <button cdsButton=\"primary\" (click)=\"showExportModal()\">\n {{ 'interface.export' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"download\" size=\"16\"></svg>\n </button>\n </ng-container>\n\n <valtimo-no-results\n [action]=\"addRoleButton\"\n description=\"{{ 'accessControl.roles.noResults.description' | translate }}\"\n title=\"{{ 'accessControl.roles.noResults.title' | translate }}\"\n ></valtimo-no-results>\n</valtimo-carbon-list>\n\n<valtimo-role-metadata-modal\n [open]=\"showAddModal$ | async\"\n (closeEvent)=\"onAdd($event)\"\n></valtimo-role-metadata-modal>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-export-role-modal\n [disabled]=\"exportDisabled$ | async\"\n [exportRowKeys]=\"obs.selectedRowKeys\"\n [open]=\"showExportModal$ | async\"\n [reset$]=\"resetExportType$\"\n (exportEvent)=\"onExport($event)\"\n (closeEvent)=\"closeExportModal()\"\n >\n </valtimo-export-role-modal>\n</ng-container>\n\n<ng-template #addRoleButton>\n <button cdsButton=\"primary\" (click)=\"openAddModal()\">\n {{ 'accessControl.roles.add' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"add\" size=\"16\"></svg>\n </button>\n</ng-template>\n" }]
460
- }], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type: i2$1.Router }], propDecorators: { carbonList: [{
599
+ }], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type: i1$2.Router }], propDecorators: { carbonList: [{
461
600
  type: ViewChild,
462
601
  args: [CarbonListComponent]
463
602
  }] } });
464
603
 
604
+ /*
605
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
606
+ *
607
+ * Licensed under EUPL, Version 1.2 (the "License");
608
+ * you may not use this file except in compliance with the License.
609
+ * You may obtain a copy of the License at
610
+ *
611
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
612
+ *
613
+ * Unless required by applicable law or agreed to in writing, software
614
+ * distributed under the License is distributed on an "AS IS" basis,
615
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
616
+ * See the License for the specific language governing permissions and
617
+ * limitations under the License.
618
+ */
619
+ const OPERATOR_LABEL = {
620
+ '==': 'accessControl.overview.operators.eq',
621
+ '!=': 'accessControl.overview.operators.neq',
622
+ '>': 'accessControl.overview.operators.gt',
623
+ '>=': 'accessControl.overview.operators.gte',
624
+ '<': 'accessControl.overview.operators.lt',
625
+ '<=': 'accessControl.overview.operators.lte',
626
+ in: 'accessControl.overview.operators.in',
627
+ list_contains: 'accessControl.overview.operators.list_contains',
628
+ };
629
+ const NO_CONTEXT_RESOURCE_TYPE = 'com.ritense.authorization.NoContext';
630
+
631
+ /*
632
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
633
+ *
634
+ * Licensed under EUPL, Version 1.2 (the "License");
635
+ * you may not use this file except in compliance with the License.
636
+ * You may obtain a copy of the License at
637
+ *
638
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
639
+ *
640
+ * Unless required by applicable law or agreed to in writing, software
641
+ * distributed under the License is distributed on an "AS IS" basis,
642
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
643
+ * See the License for the specific language governing permissions and
644
+ * limitations under the License.
645
+ */
646
+
647
+ /*
648
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
649
+ *
650
+ * Licensed under EUPL, Version 1.2 (the "License");
651
+ * you may not use this file except in compliance with the License.
652
+ * You may obtain a copy of the License at
653
+ *
654
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
655
+ *
656
+ * Unless required by applicable law or agreed to in writing, software
657
+ * distributed under the License is distributed on an "AS IS" basis,
658
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
659
+ * See the License for the specific language governing permissions and
660
+ * limitations under the License.
661
+ */
662
+ const SPECIAL_VALUE_PATTERN = /^\$\{([A-Za-z_$][A-Za-z0-9_$]*)\}$/;
663
+ function lowerFirst(s) {
664
+ return s.charAt(0).toLowerCase() + s.slice(1);
665
+ }
666
+ function upperFirst(s) {
667
+ return s.charAt(0).toUpperCase() + s.slice(1);
668
+ }
669
+ function resourceTypeI18nKey(fqn) {
670
+ const lastSegment = fqn.split('.').pop();
671
+ if (!lastSegment)
672
+ return null;
673
+ return `accessControl.resourceTypes.${lowerFirst(lastSegment)}`;
674
+ }
675
+ function fieldI18nKey(resourceType, field) {
676
+ const resourceSegment = resourceType.split('.').pop();
677
+ if (!resourceSegment || !field)
678
+ return null;
679
+ const fieldSegment = field
680
+ .split('.')
681
+ .map((part, i) => (i === 0 ? part : upperFirst(part)))
682
+ .join('');
683
+ return `accessControl.overview.fields.${lowerFirst(resourceSegment)}.${fieldSegment}`;
684
+ }
685
+ function tryTranslate(translate, key) {
686
+ const translated = translate.instant(key);
687
+ return translated === key ? null : translated;
688
+ }
689
+ function formatResourceType(translate, fqn) {
690
+ const key = resourceTypeI18nKey(fqn);
691
+ const translated = key ? tryTranslate(translate, key) : null;
692
+ if (translated !== null)
693
+ return translated;
694
+ const segments = fqn.split('.');
695
+ return segments[segments.length - 1] || fqn;
696
+ }
697
+ function formatField(translate, resourceType, field) {
698
+ const key = fieldI18nKey(resourceType, field);
699
+ const translated = key ? tryTranslate(translate, key) : null;
700
+ if (translated !== null)
701
+ return translated;
702
+ return humanizeFieldPath(field);
703
+ }
704
+ function formatOperator(translate, operator) {
705
+ const key = OPERATOR_LABEL[operator];
706
+ return key ? translate.instant(key) : operator;
707
+ }
708
+ function formatValue(translate, value) {
709
+ if (value === null) {
710
+ return translate.instant('accessControl.overview.specialValues.null');
711
+ }
712
+ if (typeof value === 'string') {
713
+ if (value === '')
714
+ return translate.instant('accessControl.overview.specialValues.empty');
715
+ const match = SPECIAL_VALUE_PATTERN.exec(value);
716
+ if (match) {
717
+ const key = `accessControl.overview.specialValues.${match[1]}`;
718
+ const translated = tryTranslate(translate, key);
719
+ if (translated !== null)
720
+ return translated;
721
+ }
722
+ return value;
723
+ }
724
+ if (Array.isArray(value)) {
725
+ return value.map(item => formatValue(translate, item)).join(', ');
726
+ }
727
+ return String(value);
728
+ }
729
+ function humanizeFieldPath(field) {
730
+ return field
731
+ .split('.')
732
+ .map(part => part.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase())
733
+ .join(' ');
734
+ }
735
+
736
+ /*
737
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
738
+ *
739
+ * Licensed under EUPL, Version 1.2 (the "License");
740
+ * you may not use this file except in compliance with the License.
741
+ * You may obtain a copy of the License at
742
+ *
743
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
744
+ *
745
+ * Unless required by applicable law or agreed to in writing, software
746
+ * distributed under the License is distributed on an "AS IS" basis,
747
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
748
+ * See the License for the specific language governing permissions and
749
+ * limitations under the License.
750
+ */
751
+
752
+ /*
753
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
754
+ *
755
+ * Licensed under EUPL, Version 1.2 (the "License");
756
+ * you may not use this file except in compliance with the License.
757
+ * You may obtain a copy of the License at
758
+ *
759
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
760
+ *
761
+ * Unless required by applicable law or agreed to in writing, software
762
+ * distributed under the License is distributed on an "AS IS" basis,
763
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
764
+ * See the License for the specific language governing permissions and
765
+ * limitations under the License.
766
+ */
767
+ class AccessControlJsonEditorTabComponent {
768
+ constructor(activatedRoute, iconService, metadataService, router, translateService) {
769
+ this.activatedRoute = activatedRoute;
770
+ this.iconService = iconService;
771
+ this.metadataService = metadataService;
772
+ this.router = router;
773
+ this.translateService = translateService;
774
+ this.disabled = false;
775
+ this.validEvent = new EventEmitter();
776
+ this.valueChangeEvent = new EventEmitter();
777
+ this._sourceModel$ = new BehaviorSubject(null);
778
+ this._filter$ = new BehaviorSubject({
779
+ resourceType: null,
780
+ action: null,
781
+ });
782
+ this._subscriptions = new Subscription();
783
+ this.isFilterActive$ = this._filter$.pipe(map(({ resourceType, action }) => !!resourceType || !!action));
784
+ this.resourceTypeItems$ = combineLatest([
785
+ this._sourceModel$,
786
+ this._filter$,
787
+ ]).pipe(map(([sourceModel, currentFilter]) => {
788
+ const permissions = this.parsePermissions(sourceModel);
789
+ const resourceTypes = Array.from(new Set(permissions.map(p => p.resourceType).filter(Boolean))).sort();
790
+ return resourceTypes.map(resourceType => ({
791
+ content: formatResourceType(this.translateService, resourceType),
792
+ resourceType,
793
+ selected: currentFilter.resourceType === resourceType,
794
+ }));
795
+ }));
796
+ this.actionItems$ = combineLatest([
797
+ this._sourceModel$,
798
+ this._filter$,
799
+ ]).pipe(map(([sourceModel, currentFilter]) => {
800
+ const permissions = this.parsePermissions(sourceModel);
801
+ const scoped = currentFilter.resourceType
802
+ ? permissions.filter(p => p.resourceType === currentFilter.resourceType)
803
+ : permissions;
804
+ const actions = Array.from(new Set(scoped.flatMap(p => [...(p.actions ?? []), ...(p.action ? [p.action] : [])]))).sort();
805
+ return actions.map(action => ({
806
+ content: this.translateService.instant(`accessControl.actions.${action}`),
807
+ action,
808
+ selected: currentFilter.action === action,
809
+ }));
810
+ }));
811
+ this.filteredModel$ = combineLatest([
812
+ this._sourceModel$,
813
+ this._filter$,
814
+ ]).pipe(map(([sourceModel, currentFilter]) => {
815
+ if (!sourceModel)
816
+ return null;
817
+ if (!currentFilter.resourceType && !currentFilter.action)
818
+ return sourceModel;
819
+ const permissions = this.parsePermissions(sourceModel);
820
+ const filtered = permissions.filter(permission => {
821
+ const resourceMatch = !currentFilter.resourceType || permission.resourceType === currentFilter.resourceType;
822
+ const actionMatch = !currentFilter.action ||
823
+ (permission.actions ?? []).includes(currentFilter.action) ||
824
+ permission.action === currentFilter.action;
825
+ return resourceMatch && actionMatch;
826
+ });
827
+ return {
828
+ ...sourceModel,
829
+ value: JSON.stringify(filtered, null, 2),
830
+ };
831
+ }));
832
+ this.editorDisabled$ = this._filter$.pipe(map(({ resourceType, action }) => !!resourceType || !!action));
833
+ this.permissionSchema$ = this.metadataService.schema$;
834
+ this.iconService.registerAll([Filter16, TrashCan16]);
835
+ }
836
+ ngOnInit() {
837
+ this.activatedRoute.queryParamMap.pipe(take(1)).subscribe(params => {
838
+ this._filter$.next({
839
+ resourceType: params.get('filterResourceType'),
840
+ action: params.get('filterAction'),
841
+ });
842
+ });
843
+ this._subscriptions.add(this._filter$.pipe(skip(1)).subscribe(currentFilter => {
844
+ this.router.navigate([], {
845
+ relativeTo: this.activatedRoute,
846
+ queryParams: {
847
+ filterResourceType: currentFilter.resourceType ?? null,
848
+ filterAction: currentFilter.action ?? null,
849
+ },
850
+ queryParamsHandling: 'merge',
851
+ replaceUrl: true,
852
+ });
853
+ }));
854
+ }
855
+ ngOnChanges(changes) {
856
+ if (changes['model']) {
857
+ this._sourceModel$.next(this.model);
858
+ }
859
+ }
860
+ ngOnDestroy() {
861
+ this._subscriptions.unsubscribe();
862
+ }
863
+ onValid(valid) {
864
+ this.validEvent.emit(valid);
865
+ }
866
+ onValueChange(value) {
867
+ if (this._filter$.getValue().resourceType || this._filter$.getValue().action)
868
+ return;
869
+ this.valueChangeEvent.emit(value);
870
+ }
871
+ onResourceTypeSelected(event) {
872
+ const resourceType = event.item?.resourceType ?? null;
873
+ this._filter$.next({ ...this._filter$.getValue(), resourceType, action: null });
874
+ }
875
+ onActionSelected(event) {
876
+ const action = event.item?.action ?? null;
877
+ this._filter$.next({ ...this._filter$.getValue(), action });
878
+ }
879
+ onClearFilter() {
880
+ this._filter$.next({ resourceType: null, action: null });
881
+ }
882
+ parsePermissions(model) {
883
+ if (!model?.value)
884
+ return [];
885
+ try {
886
+ const parsed = JSON.parse(model.value);
887
+ return Array.isArray(parsed) ? parsed : [];
888
+ }
889
+ catch {
890
+ return [];
891
+ }
892
+ }
893
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlJsonEditorTabComponent, deps: [{ token: i1$2.ActivatedRoute }, { token: i2$1.IconService }, { token: PermissionSchemaMetadataService }, { token: i1$2.Router }, { token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
894
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlJsonEditorTabComponent, isStandalone: true, selector: "valtimo-access-control-json-editor-tab", inputs: { disabled: "disabled", model: "model" }, outputs: { validEvent: "validEvent", valueChangeEvent: "valueChangeEvent" }, usesOnChanges: 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 *ngIf=\"{\n filteredModel: filteredModel$ | async,\n editorDisabled: editorDisabled$ | async,\n permissionSchema: permissionSchema$ | async,\n } as obs\"\n class=\"json-editor-tab\"\n>\n <div class=\"json-editor-tab__toolbar\">\n <v-overflow-menu placement=\"bottom-end\" [closeOnSelect]=\"false\">\n <button\n overflowTrigger\n cdsButton=\"ghost\"\n class=\"json-editor-tab__filter-trigger\"\n [class.json-editor-tab__filter-trigger--active]=\"isFilterActive$ | async\"\n [iconOnly]=\"true\"\n >\n <svg cdsIcon=\"filter\" size=\"16\"></svg>\n </button>\n\n <div overflowPane>\n <ng-container *ngTemplateOutlet=\"filterPane\"></ng-container>\n </div>\n </v-overflow-menu>\n </div>\n\n <valtimo-editor\n *ngIf=\"obs.permissionSchema\"\n [disabled]=\"obs.editorDisabled || disabled\"\n [fitPage]=\"true\"\n [jsonSchema]=\"obs.permissionSchema\"\n [model]=\"obs.filteredModel\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n</div>\n\n<ng-template #filterPane>\n <form class=\"json-editor-tab__filter\" (click)=\"$event.stopImmediatePropagation()\">\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.resourceType' | translate\"\n [placeholder]=\"'accessControl.filter.allResourceTypes' | translate\"\n (selected)=\"onResourceTypeSelected($event)\"\n >\n <cds-dropdown-list [items]=\"resourceTypeItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.action' | translate\"\n [placeholder]=\"'accessControl.filter.allActions' | translate\"\n (selected)=\"onActionSelected($event)\"\n >\n <cds-dropdown-list [items]=\"actionItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <button cdsButton=\"tertiary\" type=\"button\" (click)=\"onClearFilter()\">\n {{ 'interface.clear' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </form>\n</ng-template>\n", styles: [":host{display:block;height:100%}.json-editor-tab{display:flex;flex-direction:column;height:100%}.json-editor-tab__toolbar{display:flex;justify-content:flex-end;padding:.25rem 0}.json-editor-tab__filter-trigger{position:relative}.json-editor-tab__filter-trigger--active:after{content:\"\";position:absolute;top:.375rem;right:.375rem;width:.5rem;height:.5rem;border-radius:50%;background-color:var(--cds-support-info, #0f62fe)}.json-editor-tab__filter{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-width:16rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$1.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$1.DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: ["ariaLabel", "items", "listTpl", "type", "showTitles"], outputs: ["select", "scroll", "blurIntent"] }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i3.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage", "fitPageSpaceAdjustment"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "ngmodule", type: IconModule }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
895
+ }
896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlJsonEditorTabComponent, decorators: [{
897
+ type: Component,
898
+ args: [{ standalone: true, selector: 'valtimo-access-control-json-editor-tab', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
899
+ CommonModule,
900
+ TranslateModule,
901
+ ButtonModule,
902
+ DropdownModule,
903
+ EditorModule,
904
+ IconModule,
905
+ OverflowMenuComponent,
906
+ ], 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 *ngIf=\"{\n filteredModel: filteredModel$ | async,\n editorDisabled: editorDisabled$ | async,\n permissionSchema: permissionSchema$ | async,\n } as obs\"\n class=\"json-editor-tab\"\n>\n <div class=\"json-editor-tab__toolbar\">\n <v-overflow-menu placement=\"bottom-end\" [closeOnSelect]=\"false\">\n <button\n overflowTrigger\n cdsButton=\"ghost\"\n class=\"json-editor-tab__filter-trigger\"\n [class.json-editor-tab__filter-trigger--active]=\"isFilterActive$ | async\"\n [iconOnly]=\"true\"\n >\n <svg cdsIcon=\"filter\" size=\"16\"></svg>\n </button>\n\n <div overflowPane>\n <ng-container *ngTemplateOutlet=\"filterPane\"></ng-container>\n </div>\n </v-overflow-menu>\n </div>\n\n <valtimo-editor\n *ngIf=\"obs.permissionSchema\"\n [disabled]=\"obs.editorDisabled || disabled\"\n [fitPage]=\"true\"\n [jsonSchema]=\"obs.permissionSchema\"\n [model]=\"obs.filteredModel\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-editor>\n</div>\n\n<ng-template #filterPane>\n <form class=\"json-editor-tab__filter\" (click)=\"$event.stopImmediatePropagation()\">\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.resourceType' | translate\"\n [placeholder]=\"'accessControl.filter.allResourceTypes' | translate\"\n (selected)=\"onResourceTypeSelected($event)\"\n >\n <cds-dropdown-list [items]=\"resourceTypeItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <cds-dropdown\n [appendInline]=\"true\"\n [label]=\"'accessControl.filter.action' | translate\"\n [placeholder]=\"'accessControl.filter.allActions' | translate\"\n (selected)=\"onActionSelected($event)\"\n >\n <cds-dropdown-list [items]=\"actionItems$ | async\"></cds-dropdown-list>\n </cds-dropdown>\n\n <button cdsButton=\"tertiary\" type=\"button\" (click)=\"onClearFilter()\">\n {{ 'interface.clear' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"trash-can\" size=\"16\"></svg>\n </button>\n </form>\n</ng-template>\n", styles: [":host{display:block;height:100%}.json-editor-tab{display:flex;flex-direction:column;height:100%}.json-editor-tab__toolbar{display:flex;justify-content:flex-end;padding:.25rem 0}.json-editor-tab__filter-trigger{position:relative}.json-editor-tab__filter-trigger--active:after{content:\"\";position:absolute;top:.375rem;right:.375rem;width:.5rem;height:.5rem;border-radius:50%;background-color:var(--cds-support-info, #0f62fe)}.json-editor-tab__filter{display:flex;flex-direction:column;gap:1rem;padding:1rem;min-width:16rem}\n"] }]
907
+ }], ctorParameters: () => [{ type: i1$2.ActivatedRoute }, { type: i2$1.IconService }, { type: PermissionSchemaMetadataService }, { type: i1$2.Router }, { type: i4$1.TranslateService }], propDecorators: { disabled: [{
908
+ type: Input
909
+ }], model: [{
910
+ type: Input
911
+ }], validEvent: [{
912
+ type: Output
913
+ }], valueChangeEvent: [{
914
+ type: Output
915
+ }] } });
916
+
917
+ /*
918
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
919
+ *
920
+ * Licensed under EUPL, Version 1.2 (the "License");
921
+ * you may not use this file except in compliance with the License.
922
+ * You may obtain a copy of the License at
923
+ *
924
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
925
+ *
926
+ * Unless required by applicable law or agreed to in writing, software
927
+ * distributed under the License is distributed on an "AS IS" basis,
928
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
929
+ * See the License for the specific language governing permissions and
930
+ * limitations under the License.
931
+ */
932
+ class ResourceTypeLabelPipe {
933
+ constructor(translateService) {
934
+ this.translateService = translateService;
935
+ }
936
+ transform(fqn) {
937
+ if (!fqn)
938
+ return '';
939
+ return formatResourceType(this.translateService, fqn);
940
+ }
941
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, deps: [{ token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
942
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, isStandalone: true, name: "resourceTypeLabel" }); }
943
+ }
944
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResourceTypeLabelPipe, decorators: [{
945
+ type: Pipe,
946
+ args: [{
947
+ standalone: true,
948
+ name: 'resourceTypeLabel',
949
+ }]
950
+ }], ctorParameters: () => [{ type: i4$1.TranslateService }] });
951
+
952
+ /*
953
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
954
+ *
955
+ * Licensed under EUPL, Version 1.2 (the "License");
956
+ * you may not use this file except in compliance with the License.
957
+ * You may obtain a copy of the License at
958
+ *
959
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
960
+ *
961
+ * Unless required by applicable law or agreed to in writing, software
962
+ * distributed under the License is distributed on an "AS IS" basis,
963
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
964
+ * See the License for the specific language governing permissions and
965
+ * limitations under the License.
966
+ */
967
+
968
+ /*
969
+ * Copyright 2015-2026 Ritense BV, the Netherlands.
970
+ *
971
+ * Licensed under EUPL, Version 1.2 (the "License");
972
+ * you may not use this file except in compliance with the License.
973
+ * You may obtain a copy of the License at
974
+ *
975
+ * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
976
+ *
977
+ * Unless required by applicable law or agreed to in writing, software
978
+ * distributed under the License is distributed on an "AS IS" basis,
979
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
980
+ * See the License for the specific language governing permissions and
981
+ * limitations under the License.
982
+ */
983
+ class AccessControlOverviewTabComponent {
984
+ set permissions(value) {
985
+ this._permissions$.next(value ?? []);
986
+ }
987
+ constructor(metadataService, translateService) {
988
+ this.metadataService = metadataService;
989
+ this.translateService = translateService;
990
+ this.roleKey = null;
991
+ this._permissions$ = new BehaviorSubject([]);
992
+ this.overview$ = combineLatest([
993
+ this._permissions$,
994
+ this.metadataService.allResourceTypes$,
995
+ this.metadataService.actionsByResourceType$,
996
+ ]).pipe(map(([permissions, allResourceTypes, actionsByResourceType]) => this.buildOverview(permissions, allResourceTypes, actionsByResourceType)));
997
+ }
998
+ formatConditions(conditions, resourceType) {
999
+ return (conditions ?? []).map(condition => this.formatCondition(condition, resourceType));
1000
+ }
1001
+ isNoContext(contextResourceType) {
1002
+ return contextResourceType === NO_CONTEXT_RESOURCE_TYPE;
1003
+ }
1004
+ buildOverview(permissions, allResourceTypes, actionsByResourceType) {
1005
+ const knownResourceTypes = new Set(allResourceTypes);
1006
+ const extraResourceTypes = permissions
1007
+ .map(p => p.resourceType)
1008
+ .filter(rt => !!rt && !knownResourceTypes.has(rt));
1009
+ const resourceTypes = [...allResourceTypes, ...Array.from(new Set(extraResourceTypes))];
1010
+ return resourceTypes.map(resourceType => {
1011
+ const forResource = permissions.filter(p => p.resourceType === resourceType);
1012
+ const allowedActions = actionsByResourceType[resourceType] ?? [];
1013
+ const allowedSet = new Set(allowedActions);
1014
+ const extraActions = forResource
1015
+ .flatMap(p => [...(p.actions ?? []), ...(p.action ? [p.action] : [])])
1016
+ .filter(a => !!a && !allowedSet.has(a));
1017
+ const actions = [...allowedActions, ...Array.from(new Set(extraActions))];
1018
+ return {
1019
+ resourceType,
1020
+ actions: actions.map(action => ({
1021
+ action,
1022
+ grants: forResource.filter(p => this.permissionGrants(p, action)),
1023
+ })),
1024
+ };
1025
+ });
1026
+ }
1027
+ permissionGrants(permission, action) {
1028
+ if (permission.action === action)
1029
+ return true;
1030
+ return (permission.actions ?? []).includes(action);
1031
+ }
1032
+ formatCondition(condition, resourceType) {
1033
+ if (condition.type === 'container') {
1034
+ return this.formatContainer(condition);
1035
+ }
1036
+ if (condition.type === 'expression') {
1037
+ return this.formatExpression(condition, resourceType);
1038
+ }
1039
+ return this.formatField(condition, resourceType);
1040
+ }
1041
+ formatField(condition, resourceType) {
1042
+ return {
1043
+ kind: 'field',
1044
+ field: formatField(this.translateService, resourceType, condition.field),
1045
+ operator: formatOperator(this.translateService, condition.operator),
1046
+ value: formatValue(this.translateService, condition.value),
1047
+ customField: !this.metadataService.isFieldKnown(resourceType, condition.field),
1048
+ };
1049
+ }
1050
+ formatExpression(condition, resourceType) {
1051
+ return {
1052
+ kind: 'expression',
1053
+ field: formatField(this.translateService, resourceType, condition.field),
1054
+ path: condition.path,
1055
+ operator: formatOperator(this.translateService, condition.operator),
1056
+ value: formatValue(this.translateService, condition.value),
1057
+ clazz: condition.clazz,
1058
+ customField: !this.metadataService.isFieldKnown(resourceType, condition.field),
1059
+ };
1060
+ }
1061
+ formatContainer(condition) {
1062
+ return {
1063
+ kind: 'container',
1064
+ resourceType: condition.resourceType,
1065
+ conditions: condition.conditions ?? [],
1066
+ customResource: !this.metadataService.isResourceTypeKnown(condition.resourceType),
1067
+ };
1068
+ }
1069
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewTabComponent, deps: [{ token: PermissionSchemaMetadataService }, { token: i4$1.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
1070
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.20", type: AccessControlOverviewTabComponent, isStandalone: true, selector: "valtimo-access-control-overview-tab", inputs: { roleKey: "roleKey", permissions: "permissions" }, 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 *ngIf=\"overview$ | async as overview\" class=\"ac\">\n @for (resource of overview; track resource.resourceType) {\n <section class=\"ac__group\">\n <h4 class=\"ac__heading\">\n @if (roleKey) {\n <a\n class=\"ac__heading-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{filterResourceType: resource.resourceType}\"\n >\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n </a>\n } @else {\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n }\n </h4>\n\n @for (entry of resource.actions; track entry.action) {\n <div class=\"ac__action\">\n @if (entry.grants.length) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.can' | translate }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n\n @for (perm of entry.grants; track $index; let isFirst = $first) {\n @if (!isFirst) {\n <div class=\"ac__or ac__connective\">{{\n 'accessControl.overview.or' | translate\n }}</div>\n }\n <div class=\"ac__grant\">\n @if (perm.conditions?.length) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.when' | translate\n }}</div>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.conditions,\n resourceType: perm.resourceType,\n }\n \"\n ></ng-container>\n } @else if (!perm.contextResourceType) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.unconditional' | translate\n }}</div>\n }\n\n @if (perm.contextResourceType) {\n @if (isNoContext(perm.contextResourceType)) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.noContext' | translate\n }}</div>\n } @else {\n <div>\n <span class=\"ac__connective\">{{\n 'accessControl.overview.inContextOf' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n perm.contextResourceType | resourceTypeLabel\n }}</span>\n @if (perm.contextConditions?.length) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n }\n </div>\n @if (perm.contextConditions?.length) {\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.contextConditions,\n resourceType: perm.contextResourceType,\n }\n \"\n ></ng-container>\n }\n }\n }\n </div>\n }\n } @else {\n <span class=\"ac__no-access\">{{\n 'accessControl.overview.cannot' | translate\n }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n }\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #conditionsTpl let-conditions=\"conditions\" let-resourceType=\"resourceType\">\n <div class=\"ac__conditions\">\n @for (\n condition of formatConditions(conditions, resourceType);\n track $index;\n let isFirst = $first\n ) {\n <div class=\"ac__condition\">\n @if (!isFirst) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.and' | translate }}</span>\n }\n @switch (condition.kind) {\n @case ('field') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('expression') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.atJsonPath' | translate\n }}</span>\n <code class=\"ac__path\" [title]=\"condition.clazz\">{{ condition.path }}</code>\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('container') {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.relatedResource' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n condition.resourceType | resourceTypeLabel\n }}</span>\n @if (condition.customResource) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customResource' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: condition.conditions,\n resourceType: condition.resourceType,\n }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".ac{display:flex;flex-direction:column;gap:2rem;padding:1rem}.ac__group{display:flex;flex-direction:column;gap:.75rem}.ac__heading{border-bottom:1px solid var(--cds-border-subtle, #c6c6c6);margin:0 0 .5rem;padding-bottom:.25rem}.ac__heading-link{color:inherit;text-decoration:none}.ac__heading-link:hover,.ac__heading-link:focus-visible{text-decoration:underline}.ac__action-link{color:inherit;text-decoration:none}.ac__action-link:hover,.ac__action-link:focus-visible{text-decoration:underline}.ac__fqn{color:var(--cds-text-secondary, #525252);font-family:var(--cds-code-01-font-family, monospace);font-size:.875rem;font-weight:400;margin-left:.5rem}.ac__action{line-height:1.6}.ac__action-name,.ac__resource-type{font-weight:600}.ac__operator{font-style:italic}.ac__connective{color:var(--cds-text-secondary, #525252);font-style:italic}.ac__no-access{color:var(--cds-text-error, #da1e28);font-style:italic}.ac__grant,.ac__or,.ac__conditions{padding-left:1.5rem}.ac__path{background-color:var(--cds-layer-accent, #e0e0e0);border-radius:2px;font-family:var(--cds-code-01-font-family, monospace);padding:0 .25rem}\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: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: ResourceTypeLabelPipe, name: "resourceTypeLabel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, preserveWhitespaces: true }); }
1071
+ }
1072
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewTabComponent, decorators: [{
1073
+ type: Component,
1074
+ args: [{ standalone: true, selector: 'valtimo-access-control-overview-tab', changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: true, imports: [CommonModule, RouterModule, TranslateModule, ResourceTypeLabelPipe], 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 *ngIf=\"overview$ | async as overview\" class=\"ac\">\n @for (resource of overview; track resource.resourceType) {\n <section class=\"ac__group\">\n <h4 class=\"ac__heading\">\n @if (roleKey) {\n <a\n class=\"ac__heading-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{filterResourceType: resource.resourceType}\"\n >\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n </a>\n } @else {\n {{ resource.resourceType | resourceTypeLabel }}\n <span class=\"ac__fqn\">({{ resource.resourceType }})</span>\n }\n </h4>\n\n @for (entry of resource.actions; track entry.action) {\n <div class=\"ac__action\">\n @if (entry.grants.length) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.can' | translate }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n\n @for (perm of entry.grants; track $index; let isFirst = $first) {\n @if (!isFirst) {\n <div class=\"ac__or ac__connective\">{{\n 'accessControl.overview.or' | translate\n }}</div>\n }\n <div class=\"ac__grant\">\n @if (perm.conditions?.length) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.when' | translate\n }}</div>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.conditions,\n resourceType: perm.resourceType,\n }\n \"\n ></ng-container>\n } @else if (!perm.contextResourceType) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.unconditional' | translate\n }}</div>\n }\n\n @if (perm.contextResourceType) {\n @if (isNoContext(perm.contextResourceType)) {\n <div class=\"ac__connective\">{{\n 'accessControl.overview.noContext' | translate\n }}</div>\n } @else {\n <div>\n <span class=\"ac__connective\">{{\n 'accessControl.overview.inContextOf' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n perm.contextResourceType | resourceTypeLabel\n }}</span>\n @if (perm.contextConditions?.length) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n }\n </div>\n @if (perm.contextConditions?.length) {\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: perm.contextConditions,\n resourceType: perm.contextResourceType,\n }\n \"\n ></ng-container>\n }\n }\n }\n </div>\n }\n } @else {\n <span class=\"ac__no-access\">{{\n 'accessControl.overview.cannot' | translate\n }}</span>\n @if (roleKey) {\n <a\n class=\"ac__action-link\"\n [routerLink]=\"['/access-control', roleKey, 'json-editor']\"\n [queryParams]=\"{\n filterResourceType: resource.resourceType,\n filterAction: entry.action,\n }\"\n >\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n </a>\n } @else {\n <span class=\"ac__action-name\">{{\n 'accessControl.actions.' + entry.action | translate\n }}</span>\n }\n }\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #conditionsTpl let-conditions=\"conditions\" let-resourceType=\"resourceType\">\n <div class=\"ac__conditions\">\n @for (\n condition of formatConditions(conditions, resourceType);\n track $index;\n let isFirst = $first\n ) {\n <div class=\"ac__condition\">\n @if (!isFirst) {\n <span class=\"ac__connective\">{{ 'accessControl.overview.and' | translate }}</span>\n }\n @switch (condition.kind) {\n @case ('field') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('expression') {\n <span>{{ condition.field }}</span>\n @if (condition.customField) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customField' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.atJsonPath' | translate\n }}</span>\n <code class=\"ac__path\" [title]=\"condition.clazz\">{{ condition.path }}</code>\n <span class=\"ac__operator\">{{ condition.operator }}</span>\n <span>{{ condition.value }}</span>\n }\n\n @case ('container') {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.relatedResource' | translate\n }}</span>\n <span class=\"ac__resource-type\">{{\n condition.resourceType | resourceTypeLabel\n }}</span>\n @if (condition.customResource) {\n <span class=\"ac__connective\">{{\n 'accessControl.overview.customResource' | translate\n }}</span>\n }\n <span class=\"ac__connective\">{{\n 'accessControl.overview.where' | translate\n }}</span>\n <ng-container\n *ngTemplateOutlet=\"\n conditionsTpl;\n context: {\n conditions: condition.conditions,\n resourceType: condition.resourceType,\n }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".ac{display:flex;flex-direction:column;gap:2rem;padding:1rem}.ac__group{display:flex;flex-direction:column;gap:.75rem}.ac__heading{border-bottom:1px solid var(--cds-border-subtle, #c6c6c6);margin:0 0 .5rem;padding-bottom:.25rem}.ac__heading-link{color:inherit;text-decoration:none}.ac__heading-link:hover,.ac__heading-link:focus-visible{text-decoration:underline}.ac__action-link{color:inherit;text-decoration:none}.ac__action-link:hover,.ac__action-link:focus-visible{text-decoration:underline}.ac__fqn{color:var(--cds-text-secondary, #525252);font-family:var(--cds-code-01-font-family, monospace);font-size:.875rem;font-weight:400;margin-left:.5rem}.ac__action{line-height:1.6}.ac__action-name,.ac__resource-type{font-weight:600}.ac__operator{font-style:italic}.ac__connective{color:var(--cds-text-secondary, #525252);font-style:italic}.ac__no-access{color:var(--cds-text-error, #da1e28);font-style:italic}.ac__grant,.ac__or,.ac__conditions{padding-left:1.5rem}.ac__path{background-color:var(--cds-layer-accent, #e0e0e0);border-radius:2px;font-family:var(--cds-code-01-font-family, monospace);padding:0 .25rem}\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"] }]
1075
+ }], ctorParameters: () => [{ type: PermissionSchemaMetadataService }, { type: i4$1.TranslateService }], propDecorators: { roleKey: [{
1076
+ type: Input
1077
+ }], permissions: [{
1078
+ type: Input
1079
+ }] } });
1080
+
465
1081
  /*
466
1082
  * Copyright 2015-2025 Ritense BV, the Netherlands.
467
1083
  *
@@ -488,6 +1104,8 @@ class AccessControlEditorComponent {
488
1104
  this.accessControlExportService = accessControlExportService;
489
1105
  this.pageHeaderService = pageHeaderService;
490
1106
  this.model$ = new BehaviorSubject(null);
1107
+ this.permissions$ = new BehaviorSubject(null);
1108
+ this.roleKey$ = new BehaviorSubject(null);
491
1109
  this.saveDisabled$ = new BehaviorSubject(true);
492
1110
  this.editorDisabled$ = new BehaviorSubject(false);
493
1111
  this.moreDisabled$ = new BehaviorSubject(true);
@@ -495,9 +1113,12 @@ class AccessControlEditorComponent {
495
1113
  this.showEditModal$ = new BehaviorSubject(false);
496
1114
  this.selectedRowKeys$ = new BehaviorSubject(null);
497
1115
  this.compactMode$ = this.pageHeaderService.compactMode$;
1116
+ this.$activeTab = signal(AccessControlEditorTab.SUMMARY);
1117
+ this.AccessControlEditorTab = AccessControlEditorTab;
498
1118
  this._updatedModelValue$ = new BehaviorSubject('');
499
1119
  }
500
1120
  ngOnInit() {
1121
+ this.restoreActiveTabFromUrl();
501
1122
  this.getPermissions();
502
1123
  this.openRoleKeySubscription();
503
1124
  }
@@ -559,6 +1180,31 @@ class AccessControlEditorComponent {
559
1180
  this.showSuccessMessage(data.roleKey);
560
1181
  });
561
1182
  }
1183
+ setActiveTab(tab) {
1184
+ if (this.$activeTab() === tab)
1185
+ return;
1186
+ const roleKey = this.route.snapshot.paramMap.get('id');
1187
+ if (!roleKey) {
1188
+ this.$activeTab.set(tab);
1189
+ return;
1190
+ }
1191
+ const segments = tab === AccessControlEditorTab.JSON_EDITOR
1192
+ ? ['/access-control', roleKey, 'json-editor']
1193
+ : ['/access-control', roleKey];
1194
+ this.router.navigate(segments);
1195
+ }
1196
+ restoreActiveTabFromUrl() {
1197
+ const url = this.route.snapshot.url;
1198
+ const lastSegment = url[url.length - 1]?.path;
1199
+ if (lastSegment === 'json-editor') {
1200
+ this.$activeTab.set(AccessControlEditorTab.JSON_EDITOR);
1201
+ return;
1202
+ }
1203
+ const params = this.route.snapshot.queryParamMap;
1204
+ if (params.get('filterResourceType') || params.get('filterAction')) {
1205
+ this.$activeTab.set(AccessControlEditorTab.JSON_EDITOR);
1206
+ }
1207
+ }
562
1208
  exportPermissions() {
563
1209
  this.accessControlExportService
564
1210
  .exportRoles({ type: 'separate', roleKeys: [this._roleKey] })
@@ -568,6 +1214,7 @@ class AccessControlEditorComponent {
568
1214
  this._roleKeySubscription = this.route.params
569
1215
  .pipe(filter(params => params?.id), map(params => params.id), tap(roleKey => {
570
1216
  this._roleKey = roleKey;
1217
+ this.roleKey$.next(roleKey);
571
1218
  this.pageTitleService.setCustomPageTitle(roleKey, true);
572
1219
  this.selectedRowKeys$.next([roleKey]);
573
1220
  }), switchMap(roleKey => this.accessControlService.getRolePermissions(roleKey)), tap(permissions => {
@@ -582,6 +1229,7 @@ class AccessControlEditorComponent {
582
1229
  this.route.params
583
1230
  .pipe(tap(params => {
584
1231
  this.pageTitleService.setCustomPageTitle(params?.id);
1232
+ this.roleKey$.next(params?.id ?? null);
585
1233
  this.selectedRowKeys$.next([params?.id]);
586
1234
  }), switchMap(params => this.accessControlService.getRolePermissions(params.id)))
587
1235
  .subscribe(permissions => {
@@ -592,10 +1240,13 @@ class AccessControlEditorComponent {
592
1240
  });
593
1241
  }
594
1242
  setModel(permissions) {
1243
+ const roleKey = this.roleKey$.value ?? 'unknown';
595
1244
  this.model$.next({
596
1245
  value: JSON.stringify(permissions),
597
1246
  language: 'json',
1247
+ uri: `inmemory://access-control/role-${roleKey}.access-control-permissions.json`,
598
1248
  });
1249
+ this.permissions$.next(Array.isArray(permissions) ? permissions : null);
599
1250
  }
600
1251
  disableMore() {
601
1252
  this.moreDisabled$.next(true);
@@ -624,13 +1275,13 @@ class AccessControlEditorComponent {
624
1275
  type: 'success',
625
1276
  });
626
1277
  }
627
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, deps: [{ token: AccessControlService }, { token: i2$1.ActivatedRoute }, { token: i3$1.PageTitleService }, { token: i2$1.Router }, { token: i1.GlobalNotificationService }, { token: i5.TranslateService }, { token: AccessControlExportService }, { token: i3$1.PageHeaderService }], target: i0.ɵɵFactoryTarget.Component }); }
628
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlEditorComponent, isStandalone: false, selector: "ng-component", 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=\"model$ | async as model; else loading\">\n <valtimo-editor\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n [fitPage]=\"true\"\n ></valtimo-editor>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\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 [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}\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.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "component", type: i3$1.EditorComponent, selector: "valtimo-editor", inputs: ["editorOptions", "model", "disabled", "formatOnLoad", "widthPx", "heightPx", "heightStyle", "jsonSchema", "fitPage", "fitPageSpaceAdjustment"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "directive", type: i3$1.RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "component", type: i3.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i3$1.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i3$1.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i3$1.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1278
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, deps: [{ token: AccessControlService }, { token: i1$2.ActivatedRoute }, { token: i3.PageTitleService }, { token: i1$2.Router }, { token: i1.GlobalNotificationService }, { token: i4$1.TranslateService }, { token: AccessControlExportService }, { token: i3.PageHeaderService }], target: i0.ɵɵFactoryTarget.Component }); }
1279
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: AccessControlEditorComponent, isStandalone: false, selector: "ng-component", 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=\"model$ | async as model; else loading\">\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [heading]=\"'accessControl.roles.tabs.summary' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.SUMMARY)\"\n >\n <valtimo-access-control-overview-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [permissions]=\"permissions$ | async\"\n [roleKey]=\"roleKey$ | async\"\n ></valtimo-access-control-overview-tab>\n </cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [heading]=\"'accessControl.roles.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.JSON_EDITOR)\"\n >\n <valtimo-access-control-json-editor-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-access-control-json-editor-tab>\n </cds-tab>\n </cds-tabs>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\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 [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:#fff}\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.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "directive", type: i2$1.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "directive", type: i3.RenderInPageHeaderDirective, selector: "[renderInPageHeader]", inputs: ["fullWidth"] }, { kind: "component", type: i2$1.Loading, selector: "cds-loading, ibm-loading", inputs: ["title", "isActive", "size", "overlay"] }, { kind: "component", type: i3.OverflowMenuComponent, selector: "v-overflow-menu", inputs: ["open", "placement", "menuWidth", "offsetX", "offsetY", "closeOnSelect", "useHostAsReference", "portalToBody"], outputs: ["openChange"] }, { kind: "component", type: i3.OverflowMenuOptionComponent, selector: "v-overflow-menu-option", inputs: ["disabled", "type", "testId", "optionId"], outputs: ["selected"] }, { kind: "component", type: i3.OverflowMenuTriggerComponent, selector: "v-overflow-menu-trigger", inputs: ["compact"] }, { kind: "component", type: i2$1.Tabs, selector: "cds-tabs, ibm-tabs", inputs: ["position", "cacheActive", "followFocus", "isNavigation", "ariaLabel", "ariaLabelledby", "type", "theme", "skeleton"] }, { kind: "component", type: i2$1.Tab, selector: "cds-tab, ibm-tab", inputs: ["heading", "title", "context", "active", "disabled", "tabIndex", "id", "cacheActive", "tabContent", "templateContext"], outputs: ["selected"] }, { kind: "component", type: AccessControlJsonEditorTabComponent, selector: "valtimo-access-control-json-editor-tab", inputs: ["disabled", "model"], outputs: ["validEvent", "valueChangeEvent"] }, { kind: "component", type: AccessControlOverviewTabComponent, selector: "valtimo-access-control-overview-tab", inputs: ["roleKey", "permissions"] }, { kind: "component", type: RoleMetadataModalComponent, selector: "valtimo-role-metadata-modal", inputs: ["open", "type", "defaultKeyValue"], outputs: ["closeEvent"] }, { kind: "component", type: DeleteRoleModalComponent, selector: "valtimo-delete-role-modal", inputs: ["deleteRowKeys", "showDeleteModal$"], outputs: ["deleteEvent"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
629
1280
  }
630
1281
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, decorators: [{
631
1282
  type: Component,
632
- args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, 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=\"model$ | async as model; else loading\">\n <valtimo-editor\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n [fitPage]=\"true\"\n ></valtimo-editor>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\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 [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}\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"] }]
633
- }], ctorParameters: () => [{ type: AccessControlService }, { type: i2$1.ActivatedRoute }, { type: i3$1.PageTitleService }, { type: i2$1.Router }, { type: i1.GlobalNotificationService }, { type: i5.TranslateService }, { type: AccessControlExportService }, { type: i3$1.PageHeaderService }] });
1283
+ args: [{ standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, 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=\"model$ | async as model; else loading\">\n <cds-tabs type=\"contained\">\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [heading]=\"'accessControl.roles.tabs.summary' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.SUMMARY)\"\n >\n <valtimo-access-control-overview-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.SUMMARY\"\n [permissions]=\"permissions$ | async\"\n [roleKey]=\"roleKey$ | async\"\n ></valtimo-access-control-overview-tab>\n </cds-tab>\n\n <cds-tab\n [active]=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [heading]=\"'accessControl.roles.tabs.jsonEditor' | translate\"\n (selected)=\"setActiveTab(AccessControlEditorTab.JSON_EDITOR)\"\n >\n <valtimo-access-control-json-editor-tab\n *ngIf=\"$activeTab() === AccessControlEditorTab.JSON_EDITOR\"\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"model\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n ></valtimo-access-control-json-editor-tab>\n </cds-tab>\n </cds-tabs>\n</ng-container>\n\n<ng-container renderInPageHeader>\n <ng-template>\n <div\n *ngIf=\"{\n model: model$ | async,\n moreDisabled: moreDisabled$ | async,\n compactMode: compactMode$ | async,\n } as obs\"\n class=\"buttons-container\"\n >\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 [disabled]=\"obs.moreDisabled\" (selected)=\"showEditModal()\">{{\n 'accessControl.roles.editMetadata' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option [disabled]=\"obs.moreDisabled\" (selected)=\"exportPermissions()\">{{\n 'interface.export' | translate\n }}</v-overflow-menu-option>\n\n <v-overflow-menu-option\n [disabled]=\"obs.moreDisabled\"\n type=\"danger\"\n (selected)=\"showDeleteModal()\"\n >{{ 'interface.delete' | translate }}</v-overflow-menu-option\n >\n </v-overflow-menu>\n\n <button\n [disabled]=\"saveDisabled$ | async\"\n cdsButton=\"primary\"\n [size]=\"obs.compactMode ? 'sm' : 'md'\"\n (click)=\"updatePermissions()\"\n >\n {{ 'interface.save' | translate }}\n\n <svg class=\"cds--btn__icon\" cdsIcon=\"save\" size=\"16\"></svg>\n </button>\n </div>\n </ng-template>\n</ng-container>\n\n<ng-container *ngIf=\"{selectedRowKeys: selectedRowKeys$ | async} as obs\">\n <valtimo-delete-role-modal\n [deleteRowKeys]=\"obs.selectedRowKeys\"\n [showDeleteModal$]=\"showDeleteModal$\"\n (deleteEvent)=\"onDelete($event)\"\n >\n </valtimo-delete-role-modal>\n\n <valtimo-role-metadata-modal\n [defaultKeyValue]=\"obs.selectedRowKeys[0]\"\n [open]=\"showEditModal$ | async\"\n type=\"edit\"\n (closeEvent)=\"onEdit(obs.selectedRowKeys[0], $event)\"\n ></valtimo-role-metadata-modal>\n</ng-container>\n\n<ng-template #loading>\n <div class=\"loading-container\">\n <cds-loading></cds-loading>\n </div>\n</ng-template>\n", styles: [".loading-container{display:flex;width:100%;justify-content:center}.overflow-button{margin-right:16px}.buttons-container{display:flex;flex-direction:row;align-items:center}.buttons-container button{height:min-content}:host ::ng-deep .cds--tab-content{background-color:#fff}\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"] }]
1284
+ }], ctorParameters: () => [{ type: AccessControlService }, { type: i1$2.ActivatedRoute }, { type: i3.PageTitleService }, { type: i1$2.Router }, { type: i1.GlobalNotificationService }, { type: i4$1.TranslateService }, { type: AccessControlExportService }, { type: i3.PageHeaderService }] });
634
1285
 
635
1286
  /*
636
1287
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -664,10 +1315,20 @@ const routes = [
664
1315
  customPageTitle: true,
665
1316
  },
666
1317
  },
1318
+ {
1319
+ path: 'access-control/:id/json-editor',
1320
+ component: AccessControlEditorComponent,
1321
+ canActivate: [AuthGuardService],
1322
+ data: {
1323
+ title: 'Role details',
1324
+ roles: [ROLE_ADMIN],
1325
+ customPageTitle: true,
1326
+ },
1327
+ },
667
1328
  ];
668
1329
  class AccessControlManagementRoutingModule {
669
1330
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
670
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, i2$1.RouterModule], exports: [RouterModule] }); }
1331
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, i1$2.RouterModule], exports: [RouterModule] }); }
671
1332
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, RouterModule.forChild(routes), RouterModule] }); }
672
1333
  }
673
1334
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, decorators: [{
@@ -709,7 +1370,6 @@ class AccessControlManagementModule {
709
1370
  InputModule,
710
1371
  IconModule,
711
1372
  ConfirmationModalModule,
712
- EditorModule,
713
1373
  RenderInPageHeaderDirective,
714
1374
  LoadingModule,
715
1375
  IconModule,
@@ -717,7 +1377,10 @@ class AccessControlManagementModule {
717
1377
  OverflowMenuOptionComponent,
718
1378
  OverflowMenuTriggerComponent,
719
1379
  NotificationModule,
720
- CarbonListModule] }); }
1380
+ CarbonListModule,
1381
+ TabsModule,
1382
+ AccessControlJsonEditorTabComponent,
1383
+ AccessControlOverviewTabComponent] }); }
721
1384
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, imports: [CommonModule,
722
1385
  AccessControlManagementRoutingModule,
723
1386
  ButtonModule,
@@ -728,14 +1391,16 @@ class AccessControlManagementModule {
728
1391
  InputModule,
729
1392
  IconModule,
730
1393
  ConfirmationModalModule,
731
- EditorModule,
732
1394
  LoadingModule,
733
1395
  IconModule,
734
1396
  OverflowMenuComponent,
735
1397
  OverflowMenuOptionComponent,
736
1398
  OverflowMenuTriggerComponent,
737
1399
  NotificationModule,
738
- CarbonListModule] }); }
1400
+ CarbonListModule,
1401
+ TabsModule,
1402
+ AccessControlJsonEditorTabComponent,
1403
+ AccessControlOverviewTabComponent] }); }
739
1404
  }
740
1405
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, decorators: [{
741
1406
  type: NgModule,
@@ -758,7 +1423,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
758
1423
  InputModule,
759
1424
  IconModule,
760
1425
  ConfirmationModalModule,
761
- EditorModule,
762
1426
  RenderInPageHeaderDirective,
763
1427
  LoadingModule,
764
1428
  IconModule,
@@ -767,6 +1431,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
767
1431
  OverflowMenuTriggerComponent,
768
1432
  NotificationModule,
769
1433
  CarbonListModule,
1434
+ TabsModule,
1435
+ AccessControlJsonEditorTabComponent,
1436
+ AccessControlOverviewTabComponent,
770
1437
  ],
771
1438
  }]
772
1439
  }] });
@@ -794,5 +1461,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
794
1461
  * Generated bundle index. Do not edit.
795
1462
  */
796
1463
 
797
- export { AccessControlManagementModule, AccessControlService };
1464
+ export { AccessControlEditorTab, AccessControlManagementModule, AccessControlService, PermissionSchemaMetadataService };
798
1465
  //# sourceMappingURL=valtimo-access-control-management.mjs.map