@valtimo/access-control-management 13.31.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,60 @@
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
+ */
20
58
 
21
59
  /*
22
60
  * Copyright 2015-2026 Ritense BV, the Netherlands.
@@ -136,6 +174,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
136
174
  args: [{ providedIn: 'root' }]
137
175
  }], ctorParameters: () => [{ type: i1.ConfigService }, { type: i2.HttpClient }] });
138
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
+
139
259
  /*
140
260
  * Copyright 2015-2025 Ritense BV, the Netherlands.
141
261
  *
@@ -256,7 +376,7 @@ class RoleMetadataModalComponent {
256
376
  }, CARBON_CONSTANTS.modalAnimationMs);
257
377
  }
258
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 }); }
259
- 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 }); }
260
380
  }
261
381
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RoleMetadataModalComponent, decorators: [{
262
382
  type: Component,
@@ -294,7 +414,7 @@ class DeleteRoleModalComponent {
294
414
  this.deleteEvent.emit(roles);
295
415
  }
296
416
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
297
- 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 }); }
298
418
  }
299
419
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DeleteRoleModalComponent, decorators: [{
300
420
  type: Component,
@@ -359,7 +479,7 @@ class ExportRoleModalComponent {
359
479
  });
360
480
  }
361
481
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
362
- 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 }); }
363
483
  }
364
484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ExportRoleModalComponent, decorators: [{
365
485
  type: Component,
@@ -470,17 +590,494 @@ class AccessControlOverviewComponent {
470
590
  setSelectedRoleKeys() {
471
591
  this.selectedRowKeys$.next(this.carbonList.selectedItems.map((role) => role.roleKey));
472
592
  }
473
- 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 }); }
474
- 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 }); }
475
595
  }
476
596
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlOverviewComponent, decorators: [{
477
597
  type: Component,
478
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" }]
479
- }], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type: i2$1.Router }], propDecorators: { carbonList: [{
599
+ }], ctorParameters: () => [{ type: AccessControlService }, { type: AccessControlExportService }, { type: i1$2.Router }], propDecorators: { carbonList: [{
480
600
  type: ViewChild,
481
601
  args: [CarbonListComponent]
482
602
  }] } });
483
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
+
484
1081
  /*
485
1082
  * Copyright 2015-2025 Ritense BV, the Netherlands.
486
1083
  *
@@ -507,6 +1104,7 @@ class AccessControlEditorComponent {
507
1104
  this.accessControlExportService = accessControlExportService;
508
1105
  this.pageHeaderService = pageHeaderService;
509
1106
  this.model$ = new BehaviorSubject(null);
1107
+ this.permissions$ = new BehaviorSubject(null);
510
1108
  this.roleKey$ = new BehaviorSubject(null);
511
1109
  this.saveDisabled$ = new BehaviorSubject(true);
512
1110
  this.editorDisabled$ = new BehaviorSubject(false);
@@ -515,10 +1113,12 @@ class AccessControlEditorComponent {
515
1113
  this.showEditModal$ = new BehaviorSubject(false);
516
1114
  this.selectedRowKeys$ = new BehaviorSubject(null);
517
1115
  this.compactMode$ = this.pageHeaderService.compactMode$;
518
- this.schema$ = this.accessControlService.getPermissionSchema();
1116
+ this.$activeTab = signal(AccessControlEditorTab.SUMMARY);
1117
+ this.AccessControlEditorTab = AccessControlEditorTab;
519
1118
  this._updatedModelValue$ = new BehaviorSubject('');
520
1119
  }
521
1120
  ngOnInit() {
1121
+ this.restoreActiveTabFromUrl();
522
1122
  this.getPermissions();
523
1123
  this.openRoleKeySubscription();
524
1124
  }
@@ -580,6 +1180,31 @@ class AccessControlEditorComponent {
580
1180
  this.showSuccessMessage(data.roleKey);
581
1181
  });
582
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
+ }
583
1208
  exportPermissions() {
584
1209
  this.accessControlExportService
585
1210
  .exportRoles({ type: 'separate', roleKeys: [this._roleKey] })
@@ -621,6 +1246,7 @@ class AccessControlEditorComponent {
621
1246
  language: 'json',
622
1247
  uri: `inmemory://access-control/role-${roleKey}.access-control-permissions.json`,
623
1248
  });
1249
+ this.permissions$.next(Array.isArray(permissions) ? permissions : null);
624
1250
  }
625
1251
  disableMore() {
626
1252
  this.moreDisabled$.next(true);
@@ -649,13 +1275,13 @@ class AccessControlEditorComponent {
649
1275
  type: 'success',
650
1276
  });
651
1277
  }
652
- 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 }); }
653
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.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@if ({model: model$ | async, schema: schema$ | async}; as obs) {\n @if (obs.model && obs.schema) {\n <valtimo-editor\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"obs.model\"\n [jsonSchema]=\"obs.schema\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n [fitPage]=\"true\"\n ></valtimo-editor>\n } @else {\n <ng-container *ngTemplateOutlet=\"loading\"></ng-container>\n }\n}\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: 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.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 }); }
654
1280
  }
655
1281
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlEditorComponent, decorators: [{
656
1282
  type: Component,
657
- 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@if ({model: model$ | async, schema: schema$ | async}; as obs) {\n @if (obs.model && obs.schema) {\n <valtimo-editor\n [disabled]=\"editorDisabled$ | async\"\n [model]=\"obs.model\"\n [jsonSchema]=\"obs.schema\"\n (validEvent)=\"onValid($event)\"\n (valueChangeEvent)=\"onValueChange($event)\"\n [fitPage]=\"true\"\n ></valtimo-editor>\n } @else {\n <ng-container *ngTemplateOutlet=\"loading\"></ng-container>\n }\n}\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"] }]
658
- }], 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 }] });
659
1285
 
660
1286
  /*
661
1287
  * Copyright 2015-2025 Ritense BV, the Netherlands.
@@ -689,10 +1315,20 @@ const routes = [
689
1315
  customPageTitle: true,
690
1316
  },
691
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
+ },
692
1328
  ];
693
1329
  class AccessControlManagementRoutingModule {
694
1330
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
695
- 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] }); }
696
1332
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, imports: [CommonModule, RouterModule.forChild(routes), RouterModule] }); }
697
1333
  }
698
1334
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementRoutingModule, decorators: [{
@@ -734,7 +1370,6 @@ class AccessControlManagementModule {
734
1370
  InputModule,
735
1371
  IconModule,
736
1372
  ConfirmationModalModule,
737
- EditorModule,
738
1373
  RenderInPageHeaderDirective,
739
1374
  LoadingModule,
740
1375
  IconModule,
@@ -742,7 +1377,10 @@ class AccessControlManagementModule {
742
1377
  OverflowMenuOptionComponent,
743
1378
  OverflowMenuTriggerComponent,
744
1379
  NotificationModule,
745
- CarbonListModule] }); }
1380
+ CarbonListModule,
1381
+ TabsModule,
1382
+ AccessControlJsonEditorTabComponent,
1383
+ AccessControlOverviewTabComponent] }); }
746
1384
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, imports: [CommonModule,
747
1385
  AccessControlManagementRoutingModule,
748
1386
  ButtonModule,
@@ -753,14 +1391,16 @@ class AccessControlManagementModule {
753
1391
  InputModule,
754
1392
  IconModule,
755
1393
  ConfirmationModalModule,
756
- EditorModule,
757
1394
  LoadingModule,
758
1395
  IconModule,
759
1396
  OverflowMenuComponent,
760
1397
  OverflowMenuOptionComponent,
761
1398
  OverflowMenuTriggerComponent,
762
1399
  NotificationModule,
763
- CarbonListModule] }); }
1400
+ CarbonListModule,
1401
+ TabsModule,
1402
+ AccessControlJsonEditorTabComponent,
1403
+ AccessControlOverviewTabComponent] }); }
764
1404
  }
765
1405
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AccessControlManagementModule, decorators: [{
766
1406
  type: NgModule,
@@ -783,7 +1423,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
783
1423
  InputModule,
784
1424
  IconModule,
785
1425
  ConfirmationModalModule,
786
- EditorModule,
787
1426
  RenderInPageHeaderDirective,
788
1427
  LoadingModule,
789
1428
  IconModule,
@@ -792,6 +1431,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
792
1431
  OverflowMenuTriggerComponent,
793
1432
  NotificationModule,
794
1433
  CarbonListModule,
1434
+ TabsModule,
1435
+ AccessControlJsonEditorTabComponent,
1436
+ AccessControlOverviewTabComponent,
795
1437
  ],
796
1438
  }]
797
1439
  }] });
@@ -819,5 +1461,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
819
1461
  * Generated bundle index. Do not edit.
820
1462
  */
821
1463
 
822
- export { AccessControlManagementModule, AccessControlService };
1464
+ export { AccessControlEditorTab, AccessControlManagementModule, AccessControlService, PermissionSchemaMetadataService };
823
1465
  //# sourceMappingURL=valtimo-access-control-management.mjs.map