@c8y/ngx-components 1021.60.1 → 1021.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/core/authentication/new-password.component.d.ts +6 -3
  2. package/core/authentication/new-password.component.d.ts.map +1 -1
  3. package/core/common/interval-based-reload.abstract.d.ts +12 -1
  4. package/core/common/interval-based-reload.abstract.d.ts.map +1 -1
  5. package/core/common/permissions.service.d.ts +2 -0
  6. package/core/common/permissions.service.d.ts.map +1 -1
  7. package/core/common/tenant-ui.service.d.ts +9 -2
  8. package/core/common/tenant-ui.service.d.ts.map +1 -1
  9. package/core/forms/phone-validation.directive.d.ts +3 -6
  10. package/core/forms/phone-validation.directive.d.ts.map +1 -1
  11. package/core/router/router.module.d.ts.map +1 -1
  12. package/device-provisioned-certificates/device-provisioned-certificates.service.d.ts.map +1 -1
  13. package/esm2022/core/authentication/new-password.component.mjs +26 -5
  14. package/esm2022/core/common/interval-based-reload.abstract.mjs +50 -2
  15. package/esm2022/core/common/permissions.service.mjs +3 -1
  16. package/esm2022/core/common/tenant-ui.service.mjs +28 -6
  17. package/esm2022/core/forms/phone-validation.directive.mjs +18 -19
  18. package/esm2022/core/router/router.module.mjs +1 -5
  19. package/esm2022/core/user/user-edit.component.mjs +1 -1
  20. package/esm2022/device-provisioned-certificates/device-provisioned-certificates.service.mjs +8 -3
  21. package/esm2022/tenants/custom-properties/custom-properties.component.mjs +25 -5
  22. package/esm2022/tenants/existing-tenant.guard.mjs +18 -0
  23. package/esm2022/tenants/index.mjs +3 -1
  24. package/esm2022/tenants/support-user-access/support-user-access.component.mjs +31 -0
  25. package/esm2022/tenants/tenant-form/tenant-form-inputs-definitions.mjs +116 -0
  26. package/esm2022/tenants/tenant-form/tenant-form.component.mjs +294 -0
  27. package/esm2022/tenants/tenant-limits/tenant-limits-definitions.mjs +10 -17
  28. package/esm2022/tenants/tenant-limits/tenant-limits.component.mjs +42 -29
  29. package/esm2022/tenants/tenant-list/tenant-list.component.mjs +17 -8
  30. package/esm2022/tenants/tenants.model.mjs +30 -1
  31. package/esm2022/tenants/tenants.module.mjs +57 -22
  32. package/esm2022/translation-editor/data/translation-store.service.mjs +11 -8
  33. package/esm2022/translation-editor/index.mjs +22 -5
  34. package/esm2022/translation-editor/lazy/advanced-translation-editor/advanced-translation-editor.component.mjs +136 -0
  35. package/esm2022/translation-editor/lazy/index.mjs +2 -1
  36. package/esm2022/translation-editor/translation-editor-tab-factory.service.mjs +40 -0
  37. package/esm2022/translation-editor/translation-editor.constants.mjs +4 -0
  38. package/esm2022/upgrade/upgraded-services/index.mjs +2 -1
  39. package/esm2022/upgrade/upgraded-services/tenant-policies.service.mjs +11 -0
  40. package/esm2022/upgrade/upgraded-services/upgraded-services.module.mjs +12 -3
  41. package/esm2022/widgets/implementations/alarms/alarm-widget-alarms-reload.component.mjs +9 -7
  42. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.mjs +9 -7
  43. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs +7 -2
  44. package/fesm2022/c8y-ngx-components-device-provisioned-certificates.mjs.map +1 -1
  45. package/fesm2022/c8y-ngx-components-tenants.mjs +603 -82
  46. package/fesm2022/c8y-ngx-components-tenants.mjs.map +1 -1
  47. package/fesm2022/c8y-ngx-components-translation-editor-data.mjs +9 -6
  48. package/fesm2022/c8y-ngx-components-translation-editor-data.mjs.map +1 -1
  49. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs +127 -2
  50. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs.map +1 -1
  51. package/fesm2022/c8y-ngx-components-translation-editor.mjs +58 -4
  52. package/fesm2022/c8y-ngx-components-translation-editor.mjs.map +1 -1
  53. package/fesm2022/c8y-ngx-components-upgrade-upgraded-services.mjs +22 -3
  54. package/fesm2022/c8y-ngx-components-upgrade-upgraded-services.mjs.map +1 -1
  55. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs +8 -6
  56. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs.map +1 -1
  57. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +8 -6
  58. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -1
  59. package/fesm2022/c8y-ngx-components.mjs +499 -413
  60. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  61. package/locales/de.po +17 -25
  62. package/locales/es.po +17 -25
  63. package/locales/fr.po +17 -25
  64. package/locales/ja_JP.po +17 -25
  65. package/locales/ko.po +17 -25
  66. package/locales/locales.pot +25 -24
  67. package/locales/nl.po +17 -25
  68. package/locales/pl.po +17 -25
  69. package/locales/pt_BR.po +17 -25
  70. package/locales/zh_CN.po +17 -25
  71. package/locales/zh_TW.po +17 -25
  72. package/package.json +1 -1
  73. package/tenants/custom-properties/custom-properties.component.d.ts +4 -2
  74. package/tenants/custom-properties/custom-properties.component.d.ts.map +1 -1
  75. package/tenants/existing-tenant.guard.d.ts +13 -0
  76. package/tenants/existing-tenant.guard.d.ts.map +1 -0
  77. package/tenants/index.d.ts +2 -0
  78. package/tenants/index.d.ts.map +1 -1
  79. package/tenants/support-user-access/support-user-access.component.d.ts +18 -0
  80. package/tenants/support-user-access/support-user-access.component.d.ts.map +1 -0
  81. package/tenants/tenant-form/tenant-form-inputs-definitions.d.ts +113 -0
  82. package/tenants/tenant-form/tenant-form-inputs-definitions.d.ts.map +1 -0
  83. package/tenants/tenant-form/tenant-form.component.d.ts +79 -0
  84. package/tenants/tenant-form/tenant-form.component.d.ts.map +1 -0
  85. package/tenants/tenant-limits/tenant-limits-definitions.d.ts +11 -15
  86. package/tenants/tenant-limits/tenant-limits-definitions.d.ts.map +1 -1
  87. package/tenants/tenant-limits/tenant-limits.component.d.ts +9 -7
  88. package/tenants/tenant-limits/tenant-limits.component.d.ts.map +1 -1
  89. package/tenants/tenant-list/tenant-list.component.d.ts +6 -3
  90. package/tenants/tenant-list/tenant-list.component.d.ts.map +1 -1
  91. package/tenants/tenants.model.d.ts +31 -0
  92. package/tenants/tenants.model.d.ts.map +1 -1
  93. package/tenants/tenants.module.d.ts +4 -2
  94. package/tenants/tenants.module.d.ts.map +1 -1
  95. package/translation-editor/data/translation-store.service.d.ts +3 -2
  96. package/translation-editor/data/translation-store.service.d.ts.map +1 -1
  97. package/translation-editor/index.d.ts.map +1 -1
  98. package/translation-editor/lazy/advanced-translation-editor/advanced-translation-editor.component.d.ts +28 -0
  99. package/translation-editor/lazy/advanced-translation-editor/advanced-translation-editor.component.d.ts.map +1 -0
  100. package/translation-editor/lazy/index.d.ts +1 -0
  101. package/translation-editor/lazy/index.d.ts.map +1 -1
  102. package/translation-editor/translation-editor-tab-factory.service.d.ts +22 -0
  103. package/translation-editor/translation-editor-tab-factory.service.d.ts.map +1 -0
  104. package/translation-editor/translation-editor.constants.d.ts +4 -0
  105. package/translation-editor/translation-editor.constants.d.ts.map +1 -0
  106. package/upgrade/upgraded-services/index.d.ts +1 -0
  107. package/upgrade/upgraded-services/index.d.ts.map +1 -1
  108. package/upgrade/upgraded-services/tenant-policies.service.d.ts +11 -0
  109. package/upgrade/upgraded-services/tenant-policies.service.d.ts.map +1 -0
  110. package/upgrade/upgraded-services/upgraded-services.module.d.ts.map +1 -1
  111. package/widgets/implementations/alarms/alarm-widget-alarms-reload.component.d.ts +7 -2
  112. package/widgets/implementations/alarms/alarm-widget-alarms-reload.component.d.ts.map +1 -1
  113. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.d.ts +7 -2
  114. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.d.ts.map +1 -1
@@ -1,23 +1,24 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, Optional, Inject, Component, Input, NgModule } from '@angular/core';
3
- import * as i3$1 from '@angular/router';
2
+ import { Injectable, InjectionToken, Optional, Inject, Component, Input, ViewChild, NgModule } from '@angular/core';
3
+ import * as i5$1 from '@angular/router';
4
4
  import { RouterLink, RouterModule } from '@angular/router';
5
5
  import * as i4$1 from 'ngx-bootstrap/datepicker';
6
6
  import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
7
7
  import * as i1 from '@c8y/ngx-components';
8
- import { NavigatorNode, gettext, Permissions, BuiltInActionType, Status, ValidationPattern, C8yTranslatePipe, FormGroupComponent, FormsModule, DateTimePickerModule, CoreModule, hookNavigator, hookRoute, ViewContext } from '@c8y/ngx-components';
8
+ import { NavigatorNode, gettext, Permissions, BuiltInActionType, Status, ValidationPattern, validateInternationalPhoneNumber, CommonModule, C8yTranslatePipe, FormGroupComponent, FormsModule, DateTimePickerModule, CoreModule, hookNavigator, hookRoute, ViewContext } from '@c8y/ngx-components';
9
9
  import * as i4 from '@angular/common';
10
- import { CommonModule } from '@angular/common';
10
+ import { CommonModule as CommonModule$1 } from '@angular/common';
11
11
  import * as i3 from '@ngx-translate/core';
12
12
  import { saveAs } from 'file-saver';
13
13
  import { BehaviorSubject, from } from 'rxjs';
14
- import { expand, takeWhile, reduce, shareReplay } from 'rxjs/operators';
14
+ import { expand, takeWhile, reduce, shareReplay, take } from 'rxjs/operators';
15
15
  import * as i1$1 from '@c8y/client';
16
16
  import { TenantStatus } from '@c8y/client';
17
17
  import * as i5 from '@angular/forms';
18
- import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
18
+ import { Validators, FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
19
+ import { find, cloneDeep, assign } from 'lodash-es';
20
+ import * as i1$2 from '@c8y/ngx-components/upgrade/upgraded-services';
19
21
  import { gettext as gettext$1 } from '@c8y/ngx-components/gettext';
20
- import { get } from 'lodash-es';
21
22
 
22
23
  class TenantListGuard {
23
24
  constructor(tenantUiService) {
@@ -44,6 +45,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
44
45
  }], ctorParameters: () => [{ type: i1.TenantUiService }] });
45
46
 
46
47
  const TENANTS_MODULE_CONFIG = new InjectionToken('TenantsModuleConfig');
48
+ const PRODUCT_EXPERIENCE_TENANT_MANAGEMENT = {
49
+ EVENTS: {
50
+ TENANT_MANAGEMENT: 'tenantManagement'
51
+ },
52
+ COMPONENTS: {
53
+ TENANT_LIST: 'tenant-list',
54
+ TENANT_FORM: 'tenant-form',
55
+ TENANT_CUSTOM_PROPERTIES: 'tenant-custom-properties',
56
+ TENANT_LIMITS: 'tenant-limits'
57
+ },
58
+ ACTIONS: {
59
+ TENANT_CREATION_INITIALIZED: 'tenantCreationInitialized',
60
+ TENANT_CREATION_STARTED_FILLING: 'tenantCreationStartedFilling',
61
+ TENANT_CREATION_SAVED: 'tenantCreationSaved',
62
+ TENANT_PROPERTIES_OPENED: 'tenantPropertiesOpened',
63
+ TENANT_PROPERTIES_STARTED_CHANGING: 'tenantPropertiesStartedChanging',
64
+ TENANT_PROPERTIES_SAVED: 'tenantPropertiesSaved',
65
+ TENANT_CUSTOM_PROPERTIES_OPENED: 'tenantCustomPropertiesOpened',
66
+ TENANT_CUSTOM_PROPERTIES_STARTED_CHANGING: 'tenantCustomPropertiesStartedChanging',
67
+ TENANT_CUSTOM_PROPERTIES_SAVED: 'tenantCustomPropertiesSaved',
68
+ TENANT_LIMITS_OPENED: 'tenantLimitsOpened',
69
+ TENANT_LIMITS_STARTED_CHANGING: 'tenantLimitsStartedChanging',
70
+ TENANT_LIMITS_SAVED: 'tenantLimitsSaved'
71
+ },
72
+ RESULTS: {
73
+ SUCCESS: 'success',
74
+ FAILURE: 'failure'
75
+ }
76
+ };
47
77
 
48
78
  class TenantsNavigationFactory {
49
79
  constructor(tenantListGuard, config) {
@@ -185,8 +215,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
185
215
  args: [{ selector: 'c8y-status-filtering-form-renderer', template: "<form #filterForm=\"ngForm\">\n <div class=\"m-b-8 p-t-8\">\n <label>{{ 'Filter by status' | translate }}</label>\n <c8y-form-group class=\"m-b-0\">\n <label class=\"c8y-checkbox\">\n <input type=\"checkbox\" name=\"active\" [(ngModel)]=\"model.active\" />\n <span></span>\n <span>{{ 'Active`tenant`' | translate }}</span>\n </label>\n </c8y-form-group>\n <c8y-form-group class=\"m-b-0\">\n <label class=\"c8y-checkbox\">\n <input type=\"checkbox\" name=\"suspended\" [(ngModel)]=\"model.suspended\" />\n <span></span>\n <span>{{ 'Suspended`tenant`' | translate }}</span>\n </label>\n </c8y-form-group>\n </div>\n</form>\n\n<div class=\"data-grid__dropdown__footer d-flex separator-top\">\n <button\n class=\"btn btn-default btn-sm m-r-8 flex-grow\"\n (click)=\"resetFilter()\"\n title=\"{{ 'Reset' | translate }}\"\n >\n {{ 'Reset' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n [disabled]=\"filterForm.invalid\"\n (click)=\"applyFilter()\"\n title=\"{{ 'Apply' | translate }}\"\n >\n {{ 'Apply' | translate }}\n </button>\n</div>\n" }]
186
216
  }], ctorParameters: () => [{ type: i1.FilteringFormRendererContext }] });
187
217
 
218
+ const { EVENTS: EVENTS$3, COMPONENTS: COMPONENTS$3, ACTIONS: ACTIONS$3 } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;
188
219
  class TenantListComponent {
189
- constructor(appState, alertService, modalService, translateService, tenantService, tenantUiService, location, passwordService, userService, permissionsService) {
220
+ constructor(appState, alertService, modalService, translateService, tenantService, tenantUiService, location, passwordService, userService, permissionsService, gainsightService) {
190
221
  this.appState = appState;
191
222
  this.alertService = alertService;
192
223
  this.modalService = modalService;
@@ -197,6 +228,7 @@ class TenantListComponent {
197
228
  this.passwordService = passwordService;
198
229
  this.userService = userService;
199
230
  this.permissionsService = permissionsService;
231
+ this.gainsightService = gainsightService;
200
232
  this.tenants$ = new BehaviorSubject(undefined);
201
233
  this.isPermittedToCreateTenanant = this.permissionsService.hasAnyRole([
202
234
  Permissions.ROLE_TENANT_MANAGEMENT_ADMIN,
@@ -336,7 +368,13 @@ class TenantListComponent {
336
368
  }
337
369
  ];
338
370
  }
339
- createTenant() {
371
+ createTenant(options = { sendGainsightEvent: true }) {
372
+ if (options.sendGainsightEvent) {
373
+ this.gainsightService.triggerEvent(EVENTS$3.TENANT_MANAGEMENT, {
374
+ component: COMPONENTS$3.TENANT_LIST,
375
+ action: ACTIONS$3.TENANT_CREATION_INITIALIZED
376
+ });
377
+ }
340
378
  this.location.go('/tenants/new');
341
379
  }
342
380
  goToDetails(tenant) {
@@ -435,13 +473,425 @@ class TenantListComponent {
435
473
  const blob = new Blob([data], { type: contentType });
436
474
  saveAs(blob, filename);
437
475
  }
438
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantListComponent, deps: [{ token: i1.AppStateService }, { token: i1.AlertService }, { token: i1.ModalService }, { token: i3.TranslateService }, { token: i1$1.TenantService }, { token: i1.TenantUiService }, { token: i4.Location }, { token: i1.PasswordService }, { token: i1$1.UserService }, { token: i1.Permissions }], target: i0.ɵɵFactoryTarget.Component }); }
439
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantListComponent, selector: "c8y-tenant-list", ngImport: i0, template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "directive", type: i3$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: i1.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i1.CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: i1.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i1.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DatePipe, name: "c8yDate" }] }); }
476
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantListComponent, deps: [{ token: i1.AppStateService }, { token: i1.AlertService }, { token: i1.ModalService }, { token: i3.TranslateService }, { token: i1$1.TenantService }, { token: i1.TenantUiService }, { token: i4.Location }, { token: i1.PasswordService }, { token: i1$1.UserService }, { token: i1.Permissions }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
477
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantListComponent, selector: "c8y-tenant-list", ngImport: i0, template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant({ sendGainsightEvent: false })\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "component", type: i1.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i1.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: i1.CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: i1.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i1.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: i5$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DatePipe, name: "c8yDate" }] }); }
440
478
  }
441
479
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantListComponent, decorators: [{
442
480
  type: Component,
443
- args: [{ selector: 'c8y-tenant-list', template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n" }]
444
- }], ctorParameters: () => [{ type: i1.AppStateService }, { type: i1.AlertService }, { type: i1.ModalService }, { type: i3.TranslateService }, { type: i1$1.TenantService }, { type: i1.TenantUiService }, { type: i4.Location }, { type: i1.PasswordService }, { type: i1$1.UserService }, { type: i1.Permissions }] });
481
+ args: [{ selector: 'c8y-tenant-list', template: "<c8y-title>\n {{ 'Subtenants' | translate }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-layers\"\n label=\"{{ 'Tenants' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item\n *ngIf=\"!!(appState.state$ | async).newsletter\"\n [placement]=\"'right'\"\n>\n <button\n class=\"btn btn-link\"\n title=\"{{\n 'Downloads the list of emails of users subscribed for newsletter on the current tenant and its subtenants.'\n | translate\n }}\"\n type=\"button\"\n (click)=\"downloadNewsletterEmails()\"\n >\n <i c8yIcon=\"download\"></i>\n {{ 'Email addresses' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Create tenant' | translate }}\"\n type=\"button\"\n (click)=\"createTenant({ sendGainsightEvent: false })\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Create tenant' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#managing-subtenants\"></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"title\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel\"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [displayOptions]=\"displayOptions\"\n [columns]=\"columns\"\n [rows]=\"tenants$ | async\"\n [pagination]=\"pagination\"\n [showSearch]=\"showSearch\"\n [actionControls]=\"actionControls\"\n (onReload)=\"loadTenants()\"\n >\n <ng-container *ngIf=\"!(tenants$ | async); else empty\">\n <c8y-loading></c8y-loading>\n </ng-container>\n <ng-template #empty>\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-layers'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"\n stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\n \"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <ng-container *ngIf=\"stats?.size === 0\">\n <div>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Create tenant' | translate }}\"\n (click)=\"createTenant()\"\n [disabled]=\"!isPermittedToCreateTenanant\"\n >\n {{ 'Create tenant' | translate }}\n </button>\n </div>\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants\">user documentation</a>\n .\n </small>\n </p>\n </ng-container>\n </c8y-ui-empty-state>\n </ng-template>\n\n <c8y-column name=\"company\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n <a [routerLink]=\"['/tenants', context.item.id]\">{{ context.value }}</a>\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"parent\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value || currentTenant.name }}\">\n {{ context.value || currentTenant.name }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"creationTime\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | c8yDate }}\">\n {{ context.value | c8yDate }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"status\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ 'Active`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.ACTIVE\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n </span>\n <span\n title=\"{{ 'Suspended`tenant`' | translate }}\"\n *ngIf=\"context.item.status === TenantStatus.SUSPENDED\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n" }]
482
+ }], ctorParameters: () => [{ type: i1.AppStateService }, { type: i1.AlertService }, { type: i1.ModalService }, { type: i3.TranslateService }, { type: i1$1.TenantService }, { type: i1.TenantUiService }, { type: i4.Location }, { type: i1.PasswordService }, { type: i1$1.UserService }, { type: i1.Permissions }, { type: i1.GainsightService }] });
483
+
484
+ /**
485
+ * Define all hardTyped tenant form fields, checkboxes and select elements.
486
+ *
487
+ * while still benefiting from hard typing, thanks to "satisfies" keyword
488
+ */
489
+ const tenantPropertiesDefinitions = {
490
+ tenantID: {
491
+ id: 'tenantId',
492
+ validators: [],
493
+ defaultValue: null,
494
+ type: 'text',
495
+ label: gettext$1('ID')
496
+ },
497
+ domain: {
498
+ id: 'domain',
499
+ validators: [
500
+ Validators.required,
501
+ Validators.maxLength(32),
502
+ // TODO: To be consistent, pattern should be change for domain. It is tenantId, to avoid breaking tests, and be consistent with previous implementation.
503
+ Validators.pattern(ValidationPattern.rules.tenantId.pattern)
504
+ ],
505
+ defaultValue: null,
506
+ type: 'text',
507
+ label: gettext$1('Domain/URL'),
508
+ placeholder: gettext$1('e.g. my-tenant`used in URL`')
509
+ },
510
+ companyName: {
511
+ id: 'companyName',
512
+ validators: [
513
+ Validators.required,
514
+ Validators.maxLength(256),
515
+ Validators.pattern(ValidationPattern.rules.noWhiteSpaceAtBeginning.pattern)
516
+ ],
517
+ defaultValue: null,
518
+ type: 'text',
519
+ label: gettext$1('Name'),
520
+ placeholder: gettext$1('e.g. Company A')
521
+ },
522
+ contactName: {
523
+ id: 'contactName',
524
+ validators: [Validators.maxLength(30)],
525
+ defaultValue: null,
526
+ type: 'text',
527
+ label: gettext$1('Contact name'),
528
+ placeholder: gettext$1('e.g. Joe Doe`LOCALIZE`')
529
+ },
530
+ contactPhone: {
531
+ id: 'contactPhone',
532
+ validators: [Validators.required, validateInternationalPhoneNumber(), Validators.maxLength(20)],
533
+ defaultValue: null,
534
+ type: 'text',
535
+ label: gettext$1('Contact phone'),
536
+ placeholder: gettext$1('e.g. +49 9 876 543 210`LOCALIZE`')
537
+ },
538
+ administratorEmail: {
539
+ id: 'administratorEmail',
540
+ validators: [Validators.required, Validators.email, Validators.maxLength(256)],
541
+ defaultValue: null,
542
+ type: 'text',
543
+ label: gettext$1(`Administrator's email`),
544
+ placeholder: gettext$1('e.g. joe.doe@example.com`LOCALIZE`')
545
+ },
546
+ administratorUsername: {
547
+ id: 'administratorUsername',
548
+ validators: [
549
+ Validators.required,
550
+ Validators.pattern(ValidationPattern.rules.user.pattern),
551
+ Validators.maxLength(256)
552
+ ],
553
+ defaultValue: null,
554
+ type: 'text',
555
+ label: gettext$1(`Administrator's username`),
556
+ placeholder: gettext$1('e.g. joe`LOCALIZE`')
557
+ },
558
+ externalReference: {
559
+ id: 'externalReference',
560
+ validators: [],
561
+ defaultValue: null,
562
+ type: 'text',
563
+ label: gettext$1('External reference'),
564
+ placeholder: gettext$1('e.g. REF12345`reference number`')
565
+ },
566
+ sendPasswordResetEmail: {
567
+ id: 'sendPasswordResetEmail',
568
+ validators: [],
569
+ defaultValue: true,
570
+ type: 'checkbox',
571
+ label: gettext$1('Send password reset link as email')
572
+ },
573
+ tenantPolicy: {
574
+ id: 'tenantPolicy',
575
+ validators: [],
576
+ defaultValue: null,
577
+ type: 'select',
578
+ label: gettext$1('Tenant policy')
579
+ },
580
+ allowCreateTenants: {
581
+ id: 'allowCreateTenants',
582
+ validators: [],
583
+ defaultValue: null,
584
+ type: 'checkbox',
585
+ label: gettext$1('Allow creation of subtenants')
586
+ },
587
+ gainsightEnabled: {
588
+ id: 'gainsightEnabled',
589
+ validators: [],
590
+ defaultValue: false,
591
+ type: 'checkbox',
592
+ label: gettext$1('Enable Gainsight product experience tracking')
593
+ }
594
+ };
595
+ const tenantFormInputsDefinitions = tenantPropertiesDefinitions;
596
+
597
+ class SupportUserAccessComponent {
598
+ constructor(tenantUiService, appState, permissions) {
599
+ this.tenantUiService = tenantUiService;
600
+ this.appState = appState;
601
+ this.permissions = permissions;
602
+ }
603
+ async ngOnInit() {
604
+ this.isTopTenant = await this.tenantUiService.isManagementTenant();
605
+ this.setSupportUserLogin();
606
+ }
607
+ async setSupportUserLogin() {
608
+ const currentUser = (await this.appState.currentUser).value;
609
+ if (this.permissions.hasAnyRole([Permissions.ROLE_SUPPORT_ADMIN, Permissions.ROLE_SUPPORT_READ])) {
610
+ this.supportUserLogin = encodeURIComponent(`${currentUser.id}$`);
611
+ }
612
+ }
613
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SupportUserAccessComponent, deps: [{ token: i1.TenantUiService }, { token: i1.AppStateService }, { token: i1.Permissions }], target: i0.ɵɵFactoryTarget.Component }); }
614
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SupportUserAccessComponent, isStandalone: true, selector: "c8y-support-user-access", inputs: { tenant: "tenant" }, ngImport: i0, template: "<div class=\"card-header separator sticky-top large-padding\">\n <div class=\"card-title fit-w\">\n <i\n class=\"c8y-icon c8y-icon-c8y-support\"\n [ngClass]=\"{ 'c8y-icon-duocolor': tenant.supportUser?.enabled }\"\n ></i>\n {{ 'Support user access' | translate }}\n </div>\n <ng-container *ngIf=\"tenant.supportUser?.enabled && supportUserLogin && isTopTenant\">\n <a\n class=\"btn btn-default btn-xs\"\n title=\"{{ 'Log in' | translate }}\"\n href=\"//{{ tenant.domain }}/apps/administration/?user={{ supportUserLogin }}\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <i c8yIcon=\"external-link\"></i>\n {{ 'Log in' | translate }}\n </a>\n </ng-container>\n</div>\n<div class=\"card-block large-padding\">\n <div class=\"form-group\">\n <label>{{ 'Status' | translate }}</label>\n <div>\n <p class=\"form-control-static\">\n <span\n title=\"{{ 'Disabled' | translate }}\"\n *ngIf=\"!tenant.supportUser?.enabled\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n {{ 'Disabled' | translate }}\n </span>\n <span\n title=\"{{ 'Enabled' | translate }}\"\n *ngIf=\"tenant.supportUser?.enabled\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n {{ 'Enabled' | translate }}\n </span>\n </p>\n </div>\n </div>\n <div class=\"form-group\" data-cy=\"c8y-support-user-access--activeRequestCount\">\n <label>{{ 'Active requests count' | translate }}</label>\n\n <p class=\"form-control-static\">\n <span *ngIf=\"tenant.supportUser?.activeRequestCount\">\n <span class=\"badge badge-danger\">\n {{ tenant.supportUser?.activeRequestCount }}\n </span>\n </span>\n <span *ngIf=\"!tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0\">\n {{ 'No active requests' | translate }}\n </span>\n <span *ngIf=\"tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0\">\n {{ 'Not applicable' | translate }}\n </span>\n </p>\n </div>\n <div\n class=\"form-group\"\n *ngIf=\"tenant.supportUser?.enabled\"\n >\n <label>{{ 'Expiry date' | translate }}</label>\n <div>\n <p class=\"form-control-static\">\n <span *ngIf=\"tenant.supportUser?.expiryDate\">\n {{ tenant.supportUser?.expiryDate | c8yDate: 'medium' }}\n </span>\n <span *ngIf=\"!tenant.supportUser?.expiryDate\">{{ 'No limit' | translate }}</span>\n </p>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.DatePipe, name: "c8yDate" }] }); }
615
+ }
616
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SupportUserAccessComponent, decorators: [{
617
+ type: Component,
618
+ args: [{ selector: 'c8y-support-user-access', standalone: true, imports: [CommonModule, C8yTranslatePipe], template: "<div class=\"card-header separator sticky-top large-padding\">\n <div class=\"card-title fit-w\">\n <i\n class=\"c8y-icon c8y-icon-c8y-support\"\n [ngClass]=\"{ 'c8y-icon-duocolor': tenant.supportUser?.enabled }\"\n ></i>\n {{ 'Support user access' | translate }}\n </div>\n <ng-container *ngIf=\"tenant.supportUser?.enabled && supportUserLogin && isTopTenant\">\n <a\n class=\"btn btn-default btn-xs\"\n title=\"{{ 'Log in' | translate }}\"\n href=\"//{{ tenant.domain }}/apps/administration/?user={{ supportUserLogin }}\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <i c8yIcon=\"external-link\"></i>\n {{ 'Log in' | translate }}\n </a>\n </ng-container>\n</div>\n<div class=\"card-block large-padding\">\n <div class=\"form-group\">\n <label>{{ 'Status' | translate }}</label>\n <div>\n <p class=\"form-control-static\">\n <span\n title=\"{{ 'Disabled' | translate }}\"\n *ngIf=\"!tenant.supportUser?.enabled\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n {{ 'Disabled' | translate }}\n </span>\n <span\n title=\"{{ 'Enabled' | translate }}\"\n *ngIf=\"tenant.supportUser?.enabled\"\n >\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n {{ 'Enabled' | translate }}\n </span>\n </p>\n </div>\n </div>\n <div class=\"form-group\" data-cy=\"c8y-support-user-access--activeRequestCount\">\n <label>{{ 'Active requests count' | translate }}</label>\n\n <p class=\"form-control-static\">\n <span *ngIf=\"tenant.supportUser?.activeRequestCount\">\n <span class=\"badge badge-danger\">\n {{ tenant.supportUser?.activeRequestCount }}\n </span>\n </span>\n <span *ngIf=\"!tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0\">\n {{ 'No active requests' | translate }}\n </span>\n <span *ngIf=\"tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0\">\n {{ 'Not applicable' | translate }}\n </span>\n </p>\n </div>\n <div\n class=\"form-group\"\n *ngIf=\"tenant.supportUser?.enabled\"\n >\n <label>{{ 'Expiry date' | translate }}</label>\n <div>\n <p class=\"form-control-static\">\n <span *ngIf=\"tenant.supportUser?.expiryDate\">\n {{ tenant.supportUser?.expiryDate | c8yDate: 'medium' }}\n </span>\n <span *ngIf=\"!tenant.supportUser?.expiryDate\">{{ 'No limit' | translate }}</span>\n </p>\n </div>\n </div>\n</div>\n" }]
619
+ }], ctorParameters: () => [{ type: i1.TenantUiService }, { type: i1.AppStateService }, { type: i1.Permissions }], propDecorators: { tenant: [{
620
+ type: Input
621
+ }] } });
622
+
623
+ const { ACTIONS: ACTIONS$2, COMPONENTS: COMPONENTS$2, EVENTS: EVENTS$2, RESULTS: RESULTS$2 } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;
624
+ class TenantFormComponent {
625
+ constructor(tenantPoliciesServiceProvider, tenantService, tenantUiService, location, alertService, activatedRoute, translateService, gainsightService) {
626
+ this.tenantPoliciesServiceProvider = tenantPoliciesServiceProvider;
627
+ this.tenantService = tenantService;
628
+ this.tenantUiService = tenantUiService;
629
+ this.location = location;
630
+ this.alertService = alertService;
631
+ this.activatedRoute = activatedRoute;
632
+ this.translateService = translateService;
633
+ this.gainsightService = gainsightService;
634
+ this.MANAGEMENT_TENANT_NAME = this.tenantUiService.MANAGEMENT;
635
+ this.tenant = null;
636
+ this.tenantPolicyNone = { name: this.translateService.instant(gettext('None')) };
637
+ this.saveInProgress = false;
638
+ this.showPasswordComponent = false;
639
+ this.tenantForm = new FormGroup({});
640
+ this.fieldDefinitions = { ...tenantFormInputsDefinitions };
641
+ this.initialized = false;
642
+ }
643
+ async ngOnInit() {
644
+ await this.getHost();
645
+ this.getTenantId();
646
+ await this.getTenant();
647
+ await this.checkIfIsEnterpriseEdition();
648
+ this.initialized = true;
649
+ if (!this.isNew) {
650
+ this.sendGainsightEvent(ACTIONS$2.TENANT_PROPERTIES_OPENED);
651
+ }
652
+ this.tenantForm.valueChanges.pipe(take(1)).subscribe(() => {
653
+ this.sendGainsightEvent(this.isNew
654
+ ? ACTIONS$2.TENANT_CREATION_STARTED_FILLING
655
+ : ACTIONS$2.TENANT_PROPERTIES_STARTED_CHANGING);
656
+ });
657
+ }
658
+ async onTenantPolicyChange() {
659
+ const passwordSettings = await this.tenantUiService.getPasswordStrengthSettings();
660
+ this.tenantPolicy = find(this.tenantPolicies, {
661
+ id: this.tenantForm.controls.tenantPolicy.value.id
662
+ });
663
+ const enforcedOnSystemLevel = passwordSettings.enforceStrength;
664
+ const enforcedOnTenantLevel = passwordSettings.strengthValidity;
665
+ const enforcedOnTenantPolicyLevel = this.tenantPoliciesServiceProvider
666
+ ? await this.tenantPoliciesServiceProvider.doesEnforceStrongPassword(this.tenantPolicy)
667
+ : undefined;
668
+ if (this.isNew) {
669
+ this.passwordStrengthEnforced = enforcedOnSystemLevel || enforcedOnTenantPolicyLevel;
670
+ }
671
+ else {
672
+ this.passwordStrengthEnforced = enforcedOnSystemLevel || enforcedOnTenantLevel;
673
+ }
674
+ }
675
+ setForm() {
676
+ this.tenantForm.patchValue({
677
+ tenantId: this.tenant.id,
678
+ domain: this.tenant.domain,
679
+ companyName: this.tenant.company,
680
+ contactName: this.tenant.contactName,
681
+ contactPhone: this.tenant.contactPhone,
682
+ administratorEmail: this.tenant.adminEmail,
683
+ administratorUsername: this.tenant.adminName,
684
+ sendPasswordResetEmail: this.tenant.sendPasswordResetEmail,
685
+ allowCreateTenants: this.tenant.allowCreateTenants,
686
+ externalReference: this.tenant.customProperties.externalReference,
687
+ gainsightEnabled: this.tenant.customProperties.gainsightEnabled
688
+ });
689
+ }
690
+ getTenantId() {
691
+ this.tenantId = this.activatedRoute.snapshot.parent.data.contextData.id;
692
+ this.isNew = !this.tenantId;
693
+ }
694
+ async onSubmit() {
695
+ let tenantToBeSaved;
696
+ if (!this.isNew) {
697
+ const formRawValue = this.tenantForm.getRawValue();
698
+ const customProperties = {
699
+ ...this.tenant.customProperties,
700
+ externalReference: formRawValue.externalReference,
701
+ gainsightEnabled: formRawValue.gainsightEnabled
702
+ };
703
+ tenantToBeSaved = {
704
+ id: this.tenant.id,
705
+ company: formRawValue.companyName,
706
+ contactName: formRawValue.contactName,
707
+ contactPhone: formRawValue.contactPhone,
708
+ domain: formRawValue.domain,
709
+ status: this.tenant.status,
710
+ allowCreateTenants: this.tenantForm.value.allowCreateTenants,
711
+ customProperties: customProperties
712
+ };
713
+ }
714
+ else {
715
+ tenantToBeSaved = {
716
+ adminEmail: this.tenantForm.value.administratorEmail,
717
+ adminName: this.tenantForm.value.administratorUsername,
718
+ company: this.tenantForm.value.companyName,
719
+ contactName: this.tenantForm.value.contactName,
720
+ contactPhone: this.tenantForm.value.contactPhone,
721
+ domain: this.tenantForm.value.domain,
722
+ adminPass: this.tenantForm.value.password,
723
+ sendPasswordResetEmail: this.tenantForm.value.sendPasswordResetEmail,
724
+ tenantPolicyId: this.tenantForm.value.tenantPolicy?.id
725
+ };
726
+ }
727
+ if (this.isNew && this.host) {
728
+ tenantToBeSaved.domain = tenantToBeSaved.domain + this.host;
729
+ }
730
+ const t = cloneDeep(tenantToBeSaved);
731
+ if (t.storageLimitPerDevice === undefined) {
732
+ t.storageLimitPerDevice = 0;
733
+ }
734
+ this.setSaveInProgress(true);
735
+ try {
736
+ let savedTenant;
737
+ if (this.isNew) {
738
+ savedTenant = (await this.tenantService.create(t)).data;
739
+ if (this.tenantForm.value.allowCreateTenants) {
740
+ assign(savedTenant, { allowCreateTenants: this.tenantForm.value.allowCreateTenants });
741
+ await this.tenantService.update(savedTenant);
742
+ }
743
+ }
744
+ else {
745
+ savedTenant = (await this.tenantService.update(t)).data;
746
+ }
747
+ this.sendGainsightEvent(this.isNew ? ACTIONS$2.TENANT_CREATION_SAVED : ACTIONS$2.TENANT_PROPERTIES_SAVED, { result: RESULTS$2.SUCCESS });
748
+ this.onSave(savedTenant, this.tenantForm);
749
+ }
750
+ catch (error) {
751
+ this.alertService.addServerFailure(error);
752
+ this.sendGainsightEvent(this.isNew ? ACTIONS$2.TENANT_CREATION_SAVED : ACTIONS$2.TENANT_PROPERTIES_SAVED, { result: RESULTS$2.FAILURE, error });
753
+ }
754
+ finally {
755
+ this.setSaveInProgress(false);
756
+ }
757
+ }
758
+ onSave(tenant, form) {
759
+ if (this.isNew) {
760
+ setTimeout(() => {
761
+ this.location.go(`/tenants/${tenant.id}`);
762
+ location.reload();
763
+ }, 2000);
764
+ this.alertService.success(gettext('Tenant saved.'));
765
+ }
766
+ assign(this.tenant, tenant);
767
+ this.alertService.success(gettext('Tenant saved.'));
768
+ form.markAsPristine();
769
+ }
770
+ setSaveInProgress(value) {
771
+ this.saveInProgress = value;
772
+ }
773
+ cancel() {
774
+ this.location.go('tenants');
775
+ }
776
+ shouldDisableSave() {
777
+ const disabled = !this.tenantForm.valid || !this.tenantForm.dirty || this.saveInProgress;
778
+ if (this.showPasswordComponent) {
779
+ if (this.passwordForm) {
780
+ return disabled || !this.passwordForm.valid;
781
+ }
782
+ else {
783
+ return true;
784
+ }
785
+ }
786
+ else {
787
+ return disabled;
788
+ }
789
+ }
790
+ onNewPasswordChanged(newPassword) {
791
+ this.tenantForm.addControl('password', new FormControl(newPassword.password));
792
+ }
793
+ onCheckboxChange(inputField) {
794
+ if (inputField === 'sendPasswordResetEmail') {
795
+ this.showPasswordComponent = !this.tenantForm.value.sendPasswordResetEmail;
796
+ this.removePasswordControl(this.showPasswordComponent);
797
+ }
798
+ }
799
+ setDisabledState() {
800
+ if (!this.isNew) {
801
+ this.tenantForm.controls.tenantId.disable();
802
+ this.tenantForm.controls.domain.disable();
803
+ this.tenantForm.controls.administratorEmail.disable();
804
+ this.tenantForm.controls.administratorUsername.disable();
805
+ }
806
+ }
807
+ async shouldShowSubtenantCheckbox() {
808
+ const currentTenantIsManagement = await this.tenantUiService.isManagementTenant();
809
+ const editedTenantIsChildOfManagement = this.tenant && this.tenant.parent === this.MANAGEMENT_TENANT_NAME;
810
+ return currentTenantIsManagement && (editedTenantIsChildOfManagement || this.isNew);
811
+ }
812
+ removePasswordControl(passwordComponentShown) {
813
+ if (!passwordComponentShown) {
814
+ this.tenantForm.removeControl('password');
815
+ }
816
+ }
817
+ async getTenant() {
818
+ if (this.tenantId && this.tenantId !== 'new') {
819
+ const detailedTenant = (await this.tenantService.detail(this.tenantId)).data;
820
+ this.title = detailedTenant.company;
821
+ this.onTenant(detailedTenant);
822
+ await this.generateForm();
823
+ this.setForm();
824
+ this.setDisabledState();
825
+ }
826
+ else {
827
+ this.title = this.translateService.instant(gettext('New tenant'));
828
+ await this.generateForm();
829
+ await this.loadTenantPolicies();
830
+ }
831
+ }
832
+ async loadTenantPolicies() {
833
+ if (this.tenantPoliciesServiceProvider) {
834
+ this.onTenantPolicies(await this.tenantPoliciesServiceProvider.list());
835
+ }
836
+ }
837
+ async getHost() {
838
+ const currentTenant = await this.tenantService.current();
839
+ this.host = `.${currentTenant.data.domainName.split('.').slice(1).join('.')}`;
840
+ }
841
+ async checkIfIsEnterpriseEdition() {
842
+ this.isEnterpriseEditionTenant = await this.tenantUiService.isEnterpriseTenant();
843
+ }
844
+ onTenantPolicies(tenantPolicies) {
845
+ this.tenantPolicies = [this.tenantPolicyNone, ...tenantPolicies];
846
+ }
847
+ onTenant(tenant = {}) {
848
+ this.tenant = tenant;
849
+ }
850
+ async generateForm() {
851
+ const shouldShowSubtenantCheckbox = await this.shouldShowSubtenantCheckbox();
852
+ const gainsightAvailable = !!(await this.gainsightService.getGainsightKey());
853
+ if (!shouldShowSubtenantCheckbox) {
854
+ delete this.fieldDefinitions.allowCreateTenants;
855
+ }
856
+ if (!this.isNew) {
857
+ delete this.fieldDefinitions.sendPasswordResetEmail;
858
+ }
859
+ if (this.isNew || (this.host && this.isEnterpriseEditionTenant)) {
860
+ delete this.fieldDefinitions.tenantID;
861
+ }
862
+ if (!this.isNew && !this.host) {
863
+ delete this.fieldDefinitions.domain;
864
+ }
865
+ if (this.isNew || !gainsightAvailable) {
866
+ delete this.fieldDefinitions.gainsightEnabled;
867
+ }
868
+ if (this.isNew) {
869
+ delete this.fieldDefinitions.externalReference;
870
+ }
871
+ for (const field of Object.values(this.fieldDefinitions)) {
872
+ this.tenantForm.addControl(field.id, new FormControl(field.defaultValue, field.validators));
873
+ }
874
+ this.fieldKeys = Object.keys(this.fieldDefinitions);
875
+ }
876
+ sendGainsightEvent(action, props = {}) {
877
+ this.gainsightService.triggerEvent(EVENTS$2.TENANT_MANAGEMENT, {
878
+ component: COMPONENTS$2.TENANT_FORM,
879
+ action,
880
+ ...props
881
+ });
882
+ }
883
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantFormComponent, deps: [{ token: i1$2.Ng1TenantPoliciesService, optional: true }, { token: i1$1.TenantService }, { token: i1.TenantUiService }, { token: i4.Location }, { token: i1.AlertService }, { token: i5$1.ActivatedRoute }, { token: i3.TranslateService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
884
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantFormComponent, selector: "c8y-tenant-form", viewQueries: [{ propertyName: "passwordForm", first: true, predicate: ["passwordForm"], descendants: true }], ngImport: i0, template: "<c8y-title *ngIf=\"title\">\n {{ title }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"tenantForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card content-fullpage d-grid grid__col--8-4--md\">\n <div class=\"inner-scroll bg-level-0\">\n <div class=\"card-header separator large-padding sticky-top\">\n <div\n class=\"card-title\"\n translate\n >\n Identification\n </div>\n </div>\n\n <div>\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'select'\">\n <ng-container\n *ngTemplateOutlet=\"selectField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n </div>\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"!isNew && tenant\"\n >\n <c8y-support-user-access [tenant]=\"tenant\"></c8y-support-user-access>\n </div>\n\n <!-- FOOTER CARD -->\n <div\n class=\"card-footer separator large-padding grid__col--fullspan\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n data-cy=\"c8y-tenant-form--cancelButton\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"shouldDisableSave()\"\n data-cy=\"c8y-tenant-form--saveButton\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n\n <!-- NG TEMPLATES -->\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n <span\n class=\"input-group-addon\"\n *ngIf=\"fieldDefinition.id === 'domain' && host && isNew\"\n >\n {{ host }}\n </span>\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n (change)=\"onCheckboxChange(fieldDefinition.id)\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n <form\n #passwordForm=\"ngForm\"\n *ngIf=\"showPasswordComponent && fieldDefinition.id === 'sendPasswordResetEmail'\"\n >\n <c8y-new-password\n [showChangePasswordButton]=\"false\"\n [requireStrongPassword]=\"passwordStrengthEnforced\"\n (password)=\"onNewPasswordChanged($event)\"\n ></c8y-new-password>\n </form>\n </ng-template>\n\n <ng-template\n #selectField\n let-fieldDefinition\n >\n <c8y-form-group *ngIf=\"tenantPolicies && tenantPolicies.length > 0\">\n <label>{{ fieldDefinition.label | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n title=\"{{ fieldDefinition.label | translate }}\"\n name=\"tenantPolicy\"\n (change)=\"onTenantPolicyChange()\"\n formControlName=\"tenantPolicy\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n >\n <option\n *ngFor=\"let tenantPolicy of tenantPolicies\"\n [ngValue]=\"tenantPolicy\"\n >\n {{ tenantPolicy.name }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </ng-template>\n</form>\n", dependencies: [{ kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i1.NewPasswordComponent, selector: "c8y-new-password", inputs: ["showChangePasswordButton", "requireStrongPassword"], outputs: ["password"] }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i5.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i5.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: i5.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i5$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: SupportUserAccessComponent, selector: "c8y-support-user-access", inputs: ["tenant"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); }
885
+ }
886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantFormComponent, decorators: [{
887
+ type: Component,
888
+ args: [{ selector: 'c8y-tenant-form', template: "<c8y-title *ngIf=\"title\">\n {{ title }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"tenantForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card content-fullpage d-grid grid__col--8-4--md\">\n <div class=\"inner-scroll bg-level-0\">\n <div class=\"card-header separator large-padding sticky-top\">\n <div\n class=\"card-title\"\n translate\n >\n Identification\n </div>\n </div>\n\n <div>\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'select'\">\n <ng-container\n *ngTemplateOutlet=\"selectField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n </div>\n <div\n class=\"inner-scroll bg-level-1\"\n *ngIf=\"!isNew && tenant\"\n >\n <c8y-support-user-access [tenant]=\"tenant\"></c8y-support-user-access>\n </div>\n\n <!-- FOOTER CARD -->\n <div\n class=\"card-footer separator large-padding grid__col--fullspan\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n data-cy=\"c8y-tenant-form--cancelButton\"\n >\n {{ 'Cancel' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n [disabled]=\"shouldDisableSave()\"\n data-cy=\"c8y-tenant-form--saveButton\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n\n <!-- NG TEMPLATES -->\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <div class=\"input-group\">\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n <span\n class=\"input-group-addon\"\n *ngIf=\"fieldDefinition.id === 'domain' && host && isNew\"\n >\n {{ host }}\n </span>\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n (change)=\"onCheckboxChange(fieldDefinition.id)\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n <form\n #passwordForm=\"ngForm\"\n *ngIf=\"showPasswordComponent && fieldDefinition.id === 'sendPasswordResetEmail'\"\n >\n <c8y-new-password\n [showChangePasswordButton]=\"false\"\n [requireStrongPassword]=\"passwordStrengthEnforced\"\n (password)=\"onNewPasswordChanged($event)\"\n ></c8y-new-password>\n </form>\n </ng-template>\n\n <ng-template\n #selectField\n let-fieldDefinition\n >\n <c8y-form-group *ngIf=\"tenantPolicies && tenantPolicies.length > 0\">\n <label>{{ fieldDefinition.label | translate }}</label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n title=\"{{ fieldDefinition.label | translate }}\"\n name=\"tenantPolicy\"\n (change)=\"onTenantPolicyChange()\"\n formControlName=\"tenantPolicy\"\n [attr.data-cy]=\"'c8y-tenant-form--' + fieldDefinition.id\"\n >\n <option\n *ngFor=\"let tenantPolicy of tenantPolicies\"\n [ngValue]=\"tenantPolicy\"\n >\n {{ tenantPolicy.name }}\n </option>\n </select>\n <span></span>\n </div>\n </c8y-form-group>\n </ng-template>\n</form>\n" }]
889
+ }], ctorParameters: () => [{ type: i1$2.Ng1TenantPoliciesService, decorators: [{
890
+ type: Optional
891
+ }] }, { type: i1$1.TenantService }, { type: i1.TenantUiService }, { type: i4.Location }, { type: i1.AlertService }, { type: i5$1.ActivatedRoute }, { type: i3.TranslateService }, { type: i1.GainsightService }], propDecorators: { passwordForm: [{
892
+ type: ViewChild,
893
+ args: ['passwordForm']
894
+ }] } });
445
895
 
446
896
  const defaultFilters = {
447
897
  query: "(type eq 'c8y_JsonSchema') and (appliesTo.TENANTS eq true)",
@@ -519,12 +969,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
519
969
 
520
970
  class CustomPropertyFieldComponent {
521
971
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CustomPropertyFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
522
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CustomPropertyFieldComponent, isStandalone: true, selector: "c8y-custom-property-field", inputs: { fieldDefinition: "fieldDefinition", form: "form" }, ngImport: i0, template: "<ng-container *ngIf=\"form && fieldDefinition\">\n <ng-container [ngSwitch]=\"fieldDefinition.type\">\n <ng-container *ngSwitchCase=\"'boolean'\">\n <c8y-form-group [formGroup]=\"form\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'number'\">\n <c8y-form-group [formGroup]=\"form\">\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'integer'\">\n <c8y-form-group [formGroup]=\"form\">\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'string'\">\n <c8y-form-group\n *ngIf=\"!fieldDefinition.format\"\n [formGroup]=\"form\"\n >\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"fieldDefinition.format === 'datetime'\"\n [formGroup]=\"form\"\n >\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <c8y-date-time-picker\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n data-cy=\"c8y-custom-property-field--date-time-picker\"\n ></c8y-date-time-picker>\n </c8y-form-group>\n </ng-container>\n </ng-container>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.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: i5.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DateTimePickerModule }, { kind: "component", type: i1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }] }); }
972
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CustomPropertyFieldComponent, isStandalone: true, selector: "c8y-custom-property-field", inputs: { fieldDefinition: "fieldDefinition", form: "form" }, ngImport: i0, template: "<ng-container *ngIf=\"form && fieldDefinition\">\n <ng-container [ngSwitch]=\"fieldDefinition.type\">\n <ng-container *ngSwitchCase=\"'boolean'\">\n <c8y-form-group [formGroup]=\"form\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'number'\">\n <c8y-form-group [formGroup]=\"form\">\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'integer'\">\n <c8y-form-group [formGroup]=\"form\">\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'string'\">\n <c8y-form-group\n *ngIf=\"!fieldDefinition.format\"\n [formGroup]=\"form\"\n >\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"fieldDefinition.format === 'datetime'\"\n [formGroup]=\"form\"\n >\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate }}\n </label>\n <c8y-date-time-picker\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n data-cy=\"c8y-custom-property-field--date-time-picker\"\n ></c8y-date-time-picker>\n </c8y-form-group>\n </ng-container>\n </ng-container>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.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: i5.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: DateTimePickerModule }, { kind: "component", type: i1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }] }); }
523
973
  }
524
974
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CustomPropertyFieldComponent, decorators: [{
525
975
  type: Component,
526
976
  args: [{ selector: 'c8y-custom-property-field', standalone: true, imports: [
527
- CommonModule,
977
+ CommonModule$1,
528
978
  C8yTranslatePipe,
529
979
  FormGroupComponent,
530
980
  FormsModule,
@@ -537,12 +987,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
537
987
  type: Input
538
988
  }] } });
539
989
 
990
+ const { ACTIONS: ACTIONS$1, EVENTS: EVENTS$1, COMPONENTS: COMPONENTS$1, RESULTS: RESULTS$1 } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;
540
991
  class CustomPropertiesComponent {
541
- constructor(tenantService, alertService, activatedRoute, customPropertiesService) {
992
+ constructor(tenantService, alertService, activatedRoute, customPropertiesService, gainsightService) {
542
993
  this.tenantService = tenantService;
543
994
  this.alertService = alertService;
544
995
  this.activatedRoute = activatedRoute;
545
996
  this.customPropertiesService = customPropertiesService;
997
+ this.gainsightService = gainsightService;
546
998
  this.customPropsForm = new FormGroup({});
547
999
  this.tenant = null;
548
1000
  this.initialized = false;
@@ -554,6 +1006,10 @@ class CustomPropertiesComponent {
554
1006
  this.fieldDefinitions = fields;
555
1007
  this.applyValuesFromTenant();
556
1008
  this.initialized = true;
1009
+ this.sendGainsightEvent(ACTIONS$1.TENANT_CUSTOM_PROPERTIES_OPENED);
1010
+ this.customPropsForm.valueChanges.pipe(take(1)).subscribe(() => {
1011
+ this.sendGainsightEvent(ACTIONS$1.TENANT_CUSTOM_PROPERTIES_STARTED_CHANGING);
1012
+ });
557
1013
  }
558
1014
  async onSubmit() {
559
1015
  if (this.customPropsForm.invalid || !this.tenant) {
@@ -569,9 +1025,14 @@ class CustomPropertiesComponent {
569
1025
  try {
570
1026
  await this.tenantService.update(updatedTenant);
571
1027
  this.alertService.success(gettext('Custom properties values saved.'));
1028
+ this.sendGainsightEvent(ACTIONS$1.TENANT_CUSTOM_PROPERTIES_SAVED, { result: RESULTS$1.SUCCESS });
572
1029
  }
573
1030
  catch (error) {
574
1031
  this.alertService.addServerFailure(error);
1032
+ this.sendGainsightEvent(ACTIONS$1.TENANT_CUSTOM_PROPERTIES_SAVED, {
1033
+ result: RESULTS$1.FAILURE,
1034
+ error
1035
+ });
575
1036
  }
576
1037
  }
577
1038
  async loadTenantDetails() {
@@ -597,21 +1058,29 @@ class CustomPropertiesComponent {
597
1058
  });
598
1059
  return dirtyValues;
599
1060
  }
600
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CustomPropertiesComponent, deps: [{ token: i1$1.TenantService }, { token: i1.AlertService }, { token: i3$1.ActivatedRoute }, { token: CustomPropertiesService }], target: i0.ɵɵFactoryTarget.Component }); }
601
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CustomPropertiesComponent, isStandalone: true, selector: "c8y-custom-properties", ngImport: i0, template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<ng-container>\n <form\n [formGroup]=\"customPropsForm\"\n (ngSubmit)=\"onSubmit()\"\n >\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Custom properties\n </div>\n </div>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\"></c8y-help>\n\n <!-- empty state -->\n <c8y-ui-empty-state\n [icon]=\"'property-script'\"\n [title]=\"'No custom properties to display.' | translate\"\n [subtitle]=\"'Add a new tenant property in Properties library.' | translate\"\n *ngIf=\"fieldDefinitions?.length === 0 && initialized\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\">\n User guide\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let field of fieldDefinitions\">\n <c8y-custom-property-field\n [fieldDefinition]=\"field\"\n [form]=\"customPropsForm\"\n ></c8y-custom-property-field>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n data-cy=\"custom-properties--cancel-button\"\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!(!customPropsForm.invalid && customPropsForm.dirty)\"\n translate\n data-cy=\"custom-properties--save-button\"\n >\n Save\n </button>\n </div>\n </div>\n </form>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i1.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: CustomPropertyFieldComponent, selector: "c8y-custom-property-field", inputs: ["fieldDefinition", "form"] }] }); }
1061
+ sendGainsightEvent(action, props = {}) {
1062
+ this.gainsightService.triggerEvent(EVENTS$1.TENANT_MANAGEMENT, {
1063
+ component: COMPONENTS$1.TENANT_CUSTOM_PROPERTIES,
1064
+ action,
1065
+ ...props
1066
+ });
1067
+ }
1068
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CustomPropertiesComponent, deps: [{ token: i1$1.TenantService }, { token: i1.AlertService }, { token: i5$1.ActivatedRoute }, { token: CustomPropertiesService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1069
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CustomPropertiesComponent, isStandalone: true, selector: "c8y-custom-properties", ngImport: i0, template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<ng-container>\n <form\n [formGroup]=\"customPropsForm\"\n (ngSubmit)=\"onSubmit()\"\n >\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Custom properties\n </div>\n </div>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\"></c8y-help>\n\n <!-- empty state -->\n <c8y-ui-empty-state\n [icon]=\"'property-script'\"\n [title]=\"'No custom properties to display.' | translate\"\n [subtitle]=\"'Add a new tenant property in Properties library.' | translate\"\n *ngIf=\"fieldDefinitions?.length === 0 && initialized\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\">\n User guide\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let field of fieldDefinitions\">\n <c8y-custom-property-field\n [fieldDefinition]=\"field\"\n [form]=\"customPropsForm\"\n ></c8y-custom-property-field>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n data-cy=\"custom-properties--cancel-button\"\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!(!customPropsForm.invalid && customPropsForm.dirty)\"\n translate\n data-cy=\"custom-properties--save-button\"\n >\n Save\n </button>\n </div>\n </div>\n </form>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i1.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i1.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: CustomPropertyFieldComponent, selector: "c8y-custom-property-field", inputs: ["fieldDefinition", "form"] }] }); }
602
1070
  }
603
1071
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CustomPropertiesComponent, decorators: [{
604
1072
  type: Component,
605
1073
  args: [{ selector: 'c8y-custom-properties', standalone: true, imports: [
606
- CommonModule,
1074
+ CommonModule$1,
607
1075
  ReactiveFormsModule,
608
1076
  FormsModule,
609
1077
  CoreModule,
610
1078
  RouterLink,
611
1079
  CustomPropertyFieldComponent
612
1080
  ], template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<ng-container>\n <form\n [formGroup]=\"customPropsForm\"\n (ngSubmit)=\"onSubmit()\"\n >\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Custom properties\n </div>\n </div>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\"></c8y-help>\n\n <!-- empty state -->\n <c8y-ui-empty-state\n [icon]=\"'property-script'\"\n [title]=\"'No custom properties to display.' | translate\"\n [subtitle]=\"'Add a new tenant property in Properties library.' | translate\"\n *ngIf=\"fieldDefinitions?.length === 0 && initialized\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/enterprise-tenant/managing-tenants/#custom-properties\">\n User guide\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let field of fieldDefinitions\">\n <c8y-custom-property-field\n [fieldDefinition]=\"field\"\n [form]=\"customPropsForm\"\n ></c8y-custom-property-field>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n data-cy=\"custom-properties--cancel-button\"\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"!(!customPropsForm.invalid && customPropsForm.dirty)\"\n translate\n data-cy=\"custom-properties--save-button\"\n >\n Save\n </button>\n </div>\n </div>\n </form>\n</ng-container>\n" }]
613
- }], ctorParameters: () => [{ type: i1$1.TenantService }, { type: i1.AlertService }, { type: i3$1.ActivatedRoute }, { type: CustomPropertiesService }] });
1081
+ }], ctorParameters: () => [{ type: i1$1.TenantService }, { type: i1.AlertService }, { type: i5$1.ActivatedRoute }, { type: CustomPropertiesService }, { type: i1.GainsightService }] });
614
1082
 
1083
+ const MAX_DEVICE_STORAGE_LIMIT_MiB = Math.round(Number.MAX_SAFE_INTEGER / 1024 / 1024);
615
1084
  /**
616
1085
  * Define all hardTyped custom properties, and their configuration in one place.
617
1086
  *
@@ -619,15 +1088,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
619
1088
  * while still benefiting from hard typing, thanks to "satisfies" keyword
620
1089
  */
621
1090
  const tenantLimitsCustomPropertiesDefinition = {
622
- //TODO: External reference - going to be moved to the Properties tab.
623
- externalReference: {
624
- id: 'externalReference',
625
- validators: [],
626
- defaultValue: null,
627
- type: 'text',
628
- label: gettext$1('External reference'),
629
- placeholder: gettext$1('e.g. REF12345`reference number`')
630
- },
631
1091
  limitDevicesNumber: {
632
1092
  id: 'limit.devices.number',
633
1093
  validators: [Validators.min(0)],
@@ -691,52 +1151,66 @@ const tenantLimitsCustomPropertiesDefinition = {
691
1151
  placeholder: gettext$1('e.g. {{ example }}'),
692
1152
  placeholderArgs: { example: 100 }
693
1153
  },
694
- // TODO: Gainsight checkbox - going to be moved to the Properties tab.
695
- gainsightEnabled: {
696
- id: 'gainsightEnabled',
697
- validators: [],
698
- defaultValue: false,
699
- type: 'checkbox',
700
- label: gettext$1('Enable Gainsight product experience tracking')
1154
+ deviceStorageLimitMiB: {
1155
+ id: 'deviceStorageLimitMiB',
1156
+ validators: [Validators.min(0), Validators.max(MAX_DEVICE_STORAGE_LIMIT_MiB)],
1157
+ defaultValue: 0,
1158
+ type: 'number',
1159
+ label: gettext$1('Storage limit per device (MiB)'),
1160
+ placeholder: gettext$1('e.g. {{ example }}'),
1161
+ placeholderArgs: { example: 100 }
701
1162
  }
702
1163
  };
703
1164
  const tenantLimitsCustomProperties = tenantLimitsCustomPropertiesDefinition;
704
1165
 
1166
+ const { ACTIONS, EVENTS, COMPONENTS, RESULTS } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;
705
1167
  class TenantLimitsComponent {
706
- constructor(tenantService, tenantOptionsService, alertService, activatedRoute, applicationService) {
1168
+ constructor(tenantService, alertService, activatedRoute, applicationService, options, gainsightService) {
707
1169
  this.tenantService = tenantService;
708
- this.tenantOptionsService = tenantOptionsService;
709
1170
  this.alertService = alertService;
710
1171
  this.activatedRoute = activatedRoute;
711
1172
  this.applicationService = applicationService;
1173
+ this.options = options;
1174
+ this.gainsightService = gainsightService;
712
1175
  this.fieldDefinitions = { ...tenantLimitsCustomProperties };
713
1176
  this.limitsForm = new FormGroup({});
714
1177
  this.tenant = null;
715
1178
  this.initialized = false;
1179
+ this.deviceStorageLimitInfoMessage = gettext('Default: 0 (unlimited storage)');
716
1180
  }
717
1181
  async ngOnInit() {
718
1182
  await this.loadTenantDetails();
719
1183
  await this.setupConditionalFields();
720
1184
  this.generateForm();
721
1185
  this.initialized = true;
1186
+ this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_OPENED);
722
1187
  }
723
1188
  async onSubmit() {
724
1189
  if (this.limitsForm.invalid || !this.tenant) {
725
1190
  return;
726
1191
  }
1192
+ const { deviceStorageLimitMiB, ...customProperties } = this.getDirtyValues();
727
1193
  const updatedTenant = {
728
1194
  ...this.tenant,
729
1195
  customProperties: {
730
1196
  ...this.tenant.customProperties,
731
- ...this.getDirtyValues()
1197
+ ...customProperties
732
1198
  }
733
1199
  };
1200
+ if (this.storageLimitFeatureEnabled) {
1201
+ updatedTenant.storageLimitPerDevice = (deviceStorageLimitMiB || 0) * 1024 * 1024;
1202
+ }
1203
+ else {
1204
+ updatedTenant.storageLimitPerDevice = 0;
1205
+ }
734
1206
  try {
735
1207
  await this.tenantService.update(updatedTenant);
736
1208
  this.alertService.success(gettext('Limit values saved.'));
1209
+ this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_SAVED, { result: RESULTS.SUCCESS });
737
1210
  }
738
1211
  catch (error) {
739
1212
  this.alertService.addServerFailure(error);
1213
+ this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_SAVED, { result: RESULTS.FAILURE, error });
740
1214
  }
741
1215
  }
742
1216
  async loadTenantDetails() {
@@ -756,15 +1230,15 @@ class TenantLimitsComponent {
756
1230
  })).data;
757
1231
  const cepModuleEnabled = apps.some(app => app.name === 'cep' || app.contextPath === 'cep');
758
1232
  const dataBrokerModuleEnabled = apps.some(app => app.name === 'feature-broker' || app.contextPath === 'feature-broker');
759
- const gainsightAvailable = await this.isGainsightAvailable();
1233
+ this.storageLimitFeatureEnabled = this.options.storageLimitationFeatureEnabled;
760
1234
  if (!cepModuleEnabled) {
761
1235
  delete this.fieldDefinitions.cepServerQueueLimit;
762
1236
  }
763
1237
  if (!dataBrokerModuleEnabled) {
764
1238
  delete this.fieldDefinitions.dataBrokerQueueLimit;
765
1239
  }
766
- if (!gainsightAvailable) {
767
- delete this.fieldDefinitions.gainsightEnabled;
1240
+ if (!this.storageLimitFeatureEnabled) {
1241
+ delete this.fieldDefinitions.deviceStorageLimitMiB;
768
1242
  }
769
1243
  this.fieldKeys = Object.keys(this.fieldDefinitions);
770
1244
  }
@@ -772,27 +1246,19 @@ class TenantLimitsComponent {
772
1246
  this.alertService.addServerFailure(ex);
773
1247
  }
774
1248
  }
775
- async isGainsightAvailable() {
776
- if (get(window, 'C8Y_APP.gainsightKey')) {
777
- return true;
778
- }
779
- try {
780
- const res = await this.tenantOptionsService.detail({
781
- category: 'configuration',
782
- key: 'system.gainsight.api.key'
783
- });
784
- return !!res.data.value;
785
- }
786
- catch (error) {
787
- return false;
788
- }
789
- }
790
1249
  generateForm() {
791
1250
  for (const field of Object.values(tenantLimitsCustomProperties)) {
792
1251
  this.limitsForm.addControl(field.id, new FormControl(field.defaultValue, field.validators));
793
1252
  }
794
1253
  const customProps = this.tenant?.customProperties || {};
795
1254
  this.limitsForm.patchValue(customProps);
1255
+ if (this.storageLimitFeatureEnabled) {
1256
+ const deviceStorageLimitMiB = (this.tenant?.storageLimitPerDevice || 0) / 1024 / 1024;
1257
+ this.limitsForm.patchValue({ deviceStorageLimitMiB });
1258
+ }
1259
+ this.limitsForm.valueChanges.pipe(take(1)).subscribe(() => {
1260
+ this.sendGainsightEvent(ACTIONS.TENANT_LIMITS_STARTED_CHANGING);
1261
+ });
796
1262
  }
797
1263
  getDirtyValues() {
798
1264
  const dirtyValues = {};
@@ -804,13 +1270,36 @@ class TenantLimitsComponent {
804
1270
  });
805
1271
  return dirtyValues;
806
1272
  }
807
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantLimitsComponent, deps: [{ token: i1$1.TenantService }, { token: i1$1.TenantOptionsService }, { token: i1.AlertService }, { token: i3$1.ActivatedRoute }, { token: i1$1.ApplicationService }], target: i0.ɵɵFactoryTarget.Component }); }
808
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantLimitsComponent, isStandalone: true, selector: "c8y-tenant-limits", ngImport: i0, template: "<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.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: i5.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
1273
+ sendGainsightEvent(action, props = {}) {
1274
+ this.gainsightService.triggerEvent(EVENTS.TENANT_MANAGEMENT, {
1275
+ component: COMPONENTS.TENANT_LIMITS,
1276
+ action,
1277
+ ...props
1278
+ });
1279
+ }
1280
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantLimitsComponent, deps: [{ token: i1$1.TenantService }, { token: i1.AlertService }, { token: i5$1.ActivatedRoute }, { token: i1$1.ApplicationService }, { token: i1.OptionsService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
1281
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TenantLimitsComponent, isStandalone: true, selector: "c8y-tenant-limits", ngImport: i0, template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <ng-container\n *ngIf=\"\n fieldDefinitions.deviceStorageLimitMiB &&\n fieldDefinitions.deviceStorageLimitMiB.id === fieldDefinition.id &&\n limitsForm.controls[fieldDefinition.id] as control\n \"\n >\n <div\n class=\"icon-flex\"\n *ngIf=\"!control.dirty || (control.dirty && !control.errors)\"\n >\n <p class=\"help-block\">\n <i\n class=\"text-info m-r-4\"\n c8yIcon=\"info-circle\"\n ></i>\n <span>{{ deviceStorageLimitInfoMessage | translate }}</span>\n </p>\n </div>\n </ng-container>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n", dependencies: [{ kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.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: i5.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i5.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: i1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
809
1282
  }
810
1283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantLimitsComponent, decorators: [{
811
1284
  type: Component,
812
- args: [{ selector: 'c8y-tenant-limits', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule, CoreModule, RouterLink], template: "<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n" }]
813
- }], ctorParameters: () => [{ type: i1$1.TenantService }, { type: i1$1.TenantOptionsService }, { type: i1.AlertService }, { type: i3$1.ActivatedRoute }, { type: i1$1.ApplicationService }] });
1285
+ args: [{ selector: 'c8y-tenant-limits', standalone: true, imports: [CommonModule$1, ReactiveFormsModule, FormsModule, CoreModule, RouterLink], template: "<c8y-title *ngIf=\"tenant\">\n {{ tenant.company }}\n</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Tenants' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-layers'\"\n [label]=\"'Subtenants' | translate\"\n [path]=\"'/tenants'\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<form\n [formGroup]=\"limitsForm\"\n (ngSubmit)=\"onSubmit()\"\n>\n <div class=\"card card--fullpage m-b-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Limits\n </div>\n </div>\n\n <c8y-help src=\"/docs/enterprise-tenant/managing-tenants/#setting-limits\"></c8y-help>\n\n <div class=\"inner-scroll\">\n <div\n class=\"card-block\"\n *ngIf=\"!initialized\"\n >\n <c8y-loading></c8y-loading>\n </div>\n\n <div\n class=\"card-block\"\n *ngIf=\"initialized\"\n >\n <ng-container *ngFor=\"let key of fieldKeys\">\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'text'\">\n <ng-container\n *ngTemplateOutlet=\"textField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"fieldDefinitions[key].type === 'checkbox'\">\n <ng-container\n *ngTemplateOutlet=\"checkboxField; context: { $implicit: fieldDefinitions[key] }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div\n class=\"card-footer separator\"\n *ngIf=\"initialized\"\n >\n <button\n class=\"btn btn-default\"\n type=\"button\"\n [routerLink]=\"['/tenants']\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n type=\"submit\"\n [disabled]=\"limitsForm.invalid\"\n translate\n >\n Save\n </button>\n </div>\n </div>\n\n <ng-template\n #textField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"text\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label [for]=\"fieldDefinition.id\">\n {{ fieldDefinition.label | translate: fieldDefinition.labelArgs }}\n </label>\n <input\n class=\"form-control\"\n type=\"number\"\n [id]=\"fieldDefinition.id\"\n [placeholder]=\"fieldDefinition.placeholder | translate: fieldDefinition.placeholderArgs\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <ng-container\n *ngIf=\"\n fieldDefinitions.deviceStorageLimitMiB &&\n fieldDefinitions.deviceStorageLimitMiB.id === fieldDefinition.id &&\n limitsForm.controls[fieldDefinition.id] as control\n \"\n >\n <div\n class=\"icon-flex\"\n *ngIf=\"!control.dirty || (control.dirty && !control.errors)\"\n >\n <p class=\"help-block\">\n <i\n class=\"text-info m-r-4\"\n c8yIcon=\"info-circle\"\n ></i>\n <span>{{ deviceStorageLimitInfoMessage | translate }}</span>\n </p>\n </div>\n </ng-container>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #checkboxField\n let-fieldDefinition\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n [title]=\"fieldDefinition.label | translate\"\n [for]=\"fieldDefinition.id\"\n >\n <input\n type=\"checkbox\"\n [id]=\"fieldDefinition.id\"\n [formControlName]=\"fieldDefinition.id\"\n />\n <span></span>\n <span>{{ fieldDefinition.label | translate }}</span>\n </label>\n </c8y-form-group>\n </ng-template>\n</form>\n" }]
1286
+ }], ctorParameters: () => [{ type: i1$1.TenantService }, { type: i1.AlertService }, { type: i5$1.ActivatedRoute }, { type: i1$1.ApplicationService }, { type: i1.OptionsService }, { type: i1.GainsightService }] });
1287
+
1288
+ class ExistingTenantGuard {
1289
+ /**
1290
+ * Checks if the current route is displayed for an existing tenant.
1291
+ *
1292
+ * @returns `true` if visiting a route for an existing tenant.
1293
+ */
1294
+ canActivate(route) {
1295
+ return route.params.id !== 'new';
1296
+ }
1297
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ExistingTenantGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1298
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ExistingTenantGuard }); }
1299
+ }
1300
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ExistingTenantGuard, decorators: [{
1301
+ type: Injectable
1302
+ }] });
814
1303
 
815
1304
  class TenantsModule {
816
1305
  static config(config = {}) {
@@ -827,24 +1316,39 @@ class TenantsModule {
827
1316
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
828
1317
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TenantsModule, declarations: [TenantListComponent,
829
1318
  CreationTimeFilteringFormRendererComponent,
830
- StatusFilteringFormRendererComponent], imports: [RouterModule, BsDatepickerModule, CoreModule] }); }
1319
+ StatusFilteringFormRendererComponent,
1320
+ TenantFormComponent], imports: [BsDatepickerModule, CoreModule, RouterModule, SupportUserAccessComponent] }); }
831
1321
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantsModule, providers: [
832
1322
  TenantListGuard,
1323
+ ExistingTenantGuard,
833
1324
  hookNavigator(TenantsNavigationFactory),
834
- hookRoute({
835
- path: 'tenants',
836
- icon: 'c8y-layers',
837
- component: TenantListComponent,
838
- canActivate: [TenantListGuard]
839
- }),
840
1325
  hookRoute([
1326
+ {
1327
+ path: 'tenants',
1328
+ icon: 'c8y-layers',
1329
+ component: TenantListComponent,
1330
+ canActivate: [TenantListGuard]
1331
+ },
1332
+ {
1333
+ path: 'tenants/new',
1334
+ component: TenantFormComponent
1335
+ },
1336
+ {
1337
+ path: 'properties',
1338
+ component: TenantFormComponent,
1339
+ icon: 'new-property',
1340
+ label: gettext('Properties'),
1341
+ context: ViewContext.Tenant,
1342
+ priority: 10000
1343
+ },
841
1344
  {
842
1345
  path: 'custom_properties',
843
1346
  component: CustomPropertiesComponent,
844
1347
  icon: 'property-script',
845
1348
  label: gettext('Custom properties'),
846
1349
  context: ViewContext.Tenant,
847
- priority: 4
1350
+ priority: 4,
1351
+ canActivate: [ExistingTenantGuard]
848
1352
  },
849
1353
  {
850
1354
  path: 'limits',
@@ -852,38 +1356,54 @@ class TenantsModule {
852
1356
  icon: 'sliders',
853
1357
  label: gettext('Limits'),
854
1358
  context: ViewContext.Tenant,
855
- priority: 3
1359
+ priority: 3,
1360
+ canActivate: [ExistingTenantGuard]
856
1361
  }
857
1362
  ])
858
- ], imports: [RouterModule, BsDatepickerModule, CoreModule] }); }
1363
+ ], imports: [BsDatepickerModule, CoreModule, RouterModule, SupportUserAccessComponent] }); }
859
1364
  }
860
1365
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TenantsModule, decorators: [{
861
1366
  type: NgModule,
862
1367
  args: [{
863
- imports: [RouterModule, BsDatepickerModule, CoreModule],
1368
+ imports: [BsDatepickerModule, CoreModule, RouterModule, SupportUserAccessComponent],
864
1369
  exports: [],
865
1370
  declarations: [
866
1371
  TenantListComponent,
867
1372
  CreationTimeFilteringFormRendererComponent,
868
- StatusFilteringFormRendererComponent
1373
+ StatusFilteringFormRendererComponent,
1374
+ TenantFormComponent
869
1375
  ],
870
1376
  providers: [
871
1377
  TenantListGuard,
1378
+ ExistingTenantGuard,
872
1379
  hookNavigator(TenantsNavigationFactory),
873
- hookRoute({
874
- path: 'tenants',
875
- icon: 'c8y-layers',
876
- component: TenantListComponent,
877
- canActivate: [TenantListGuard]
878
- }),
879
1380
  hookRoute([
1381
+ {
1382
+ path: 'tenants',
1383
+ icon: 'c8y-layers',
1384
+ component: TenantListComponent,
1385
+ canActivate: [TenantListGuard]
1386
+ },
1387
+ {
1388
+ path: 'tenants/new',
1389
+ component: TenantFormComponent
1390
+ },
1391
+ {
1392
+ path: 'properties',
1393
+ component: TenantFormComponent,
1394
+ icon: 'new-property',
1395
+ label: gettext('Properties'),
1396
+ context: ViewContext.Tenant,
1397
+ priority: 10000
1398
+ },
880
1399
  {
881
1400
  path: 'custom_properties',
882
1401
  component: CustomPropertiesComponent,
883
1402
  icon: 'property-script',
884
1403
  label: gettext('Custom properties'),
885
1404
  context: ViewContext.Tenant,
886
- priority: 4
1405
+ priority: 4,
1406
+ canActivate: [ExistingTenantGuard]
887
1407
  },
888
1408
  {
889
1409
  path: 'limits',
@@ -891,7 +1411,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
891
1411
  icon: 'sliders',
892
1412
  label: gettext('Limits'),
893
1413
  context: ViewContext.Tenant,
894
- priority: 3
1414
+ priority: 3,
1415
+ canActivate: [ExistingTenantGuard]
895
1416
  }
896
1417
  ])
897
1418
  ]
@@ -902,5 +1423,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
902
1423
  * Generated bundle index. Do not edit.
903
1424
  */
904
1425
 
905
- export { CreationTimeFilteringFormRendererComponent, CustomPropertiesComponent, StatusFilteringFormRendererComponent, TENANTS_MODULE_CONFIG, TenantListComponent, TenantListGuard, TenantsModule, TenantsNavigationFactory };
1426
+ export { CreationTimeFilteringFormRendererComponent, CustomPropertiesComponent, PRODUCT_EXPERIENCE_TENANT_MANAGEMENT, StatusFilteringFormRendererComponent, SupportUserAccessComponent, TENANTS_MODULE_CONFIG, TenantFormComponent, TenantListComponent, TenantListGuard, TenantsModule, TenantsNavigationFactory };
906
1427
  //# sourceMappingURL=c8y-ngx-components-tenants.mjs.map