@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
1 lines • 150 kB
Source Map (JSON)
{"version":3,"file":"c8y-ngx-components-tenants.mjs","sources":["../../tenants/tenant-list/tenant-list.guard.ts","../../tenants/tenants.model.ts","../../tenants/tenants-navigation.factory.ts","../../tenants/tenant-list/creation-time.filtering-form-renderer.component.ts","../../tenants/tenant-list/creation-time.filtering-form-renderer.component.html","../../tenants/tenant-list/status.filtering-form-renderer.component.ts","../../tenants/tenant-list/status.filtering-form-renderer.component.html","../../tenants/tenant-list/tenant-list.component.ts","../../tenants/tenant-list/tenant-list.component.html","../../tenants/tenant-form/tenant-form-inputs-definitions.ts","../../tenants/support-user-access/support-user-access.component.ts","../../tenants/support-user-access/support-user-access.component.html","../../tenants/tenant-form/tenant-form.component.ts","../../tenants/tenant-form/tenant-form.component.html","../../tenants/custom-properties/custom-properties.service.ts","../../tenants/custom-properties/custom-property-field/custom-property-field.component.ts","../../tenants/custom-properties/custom-property-field/custom-property-field.component.html","../../tenants/custom-properties/custom-properties.component.ts","../../tenants/custom-properties/custom-properties.component.html","../../tenants/tenant-limits/tenant-limits-definitions.ts","../../tenants/tenant-limits/tenant-limits.component.ts","../../tenants/tenant-limits/tenant-limits.component.html","../../tenants/existing-tenant.guard.ts","../../tenants/tenants.module.ts","../../tenants/c8y-ngx-components-tenants.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\nimport { TenantUiService } from '@c8y/ngx-components';\n\n@Injectable()\nexport class TenantListGuard {\n private active: boolean;\n\n constructor(private tenantUiService: TenantUiService) {}\n\n /**\n * Checks if tenant list should be active,\n * i.e. whether the current tenant can read other tenants.\n * **Note: the check is executed only once in the runtime.**\n *\n * @returns True, if the feature should be active.\n */\n canActivate(): boolean {\n if (this.active === undefined) {\n this.active = this.tenantUiService.canReadTenants();\n }\n return this.active;\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { NavigatorNode } from '@c8y/ngx-components';\n\nexport const TENANTS_MODULE_CONFIG = new InjectionToken<TenantsModuleConfig>('TenantsModuleConfig');\n\n/**\n * Configuration object for `TenantsModule`.\n */\nexport interface TenantsModuleConfig {\n /**\n * Allows for hiding or customizing \"Subtenants\" navigator node:\n * - `subtenantsNavigatorNode: true` - shows the default navigator node (default),\n * - `subtenantsNavigatorNode: false` - hides the navigator node,\n * - `subtenantsNavigatorNode: { label: 'My subtenants' }` - overrides default navigator node,\n * - `subtenantsNavigatorNode: new NavigatorNode({ label: 'My subtenants' })` - overrides default navigator node.\n */\n subtenantsNavigatorNode?: boolean | Partial<NavigatorNode>;\n}\n\nexport const PRODUCT_EXPERIENCE_TENANT_MANAGEMENT = {\n EVENTS: {\n TENANT_MANAGEMENT: 'tenantManagement'\n },\n COMPONENTS: {\n TENANT_LIST: 'tenant-list',\n TENANT_FORM: 'tenant-form',\n TENANT_CUSTOM_PROPERTIES: 'tenant-custom-properties',\n TENANT_LIMITS: 'tenant-limits'\n },\n ACTIONS: {\n TENANT_CREATION_INITIALIZED: 'tenantCreationInitialized',\n TENANT_CREATION_STARTED_FILLING: 'tenantCreationStartedFilling',\n TENANT_CREATION_SAVED: 'tenantCreationSaved',\n TENANT_PROPERTIES_OPENED: 'tenantPropertiesOpened',\n TENANT_PROPERTIES_STARTED_CHANGING: 'tenantPropertiesStartedChanging',\n TENANT_PROPERTIES_SAVED: 'tenantPropertiesSaved',\n TENANT_CUSTOM_PROPERTIES_OPENED: 'tenantCustomPropertiesOpened',\n TENANT_CUSTOM_PROPERTIES_STARTED_CHANGING: 'tenantCustomPropertiesStartedChanging',\n TENANT_CUSTOM_PROPERTIES_SAVED: 'tenantCustomPropertiesSaved',\n TENANT_LIMITS_OPENED: 'tenantLimitsOpened',\n TENANT_LIMITS_STARTED_CHANGING: 'tenantLimitsStartedChanging',\n TENANT_LIMITS_SAVED: 'tenantLimitsSaved'\n },\n RESULTS: {\n SUCCESS: 'success',\n FAILURE: 'failure'\n }\n} as const;\nexport type TenantManagementActionType =\n (typeof PRODUCT_EXPERIENCE_TENANT_MANAGEMENT.ACTIONS)[keyof typeof PRODUCT_EXPERIENCE_TENANT_MANAGEMENT.ACTIONS];\nexport type TenantManagementResultType =\n (typeof PRODUCT_EXPERIENCE_TENANT_MANAGEMENT.RESULTS)[keyof typeof PRODUCT_EXPERIENCE_TENANT_MANAGEMENT.RESULTS];\n","import { Inject, Injectable, Optional } from '@angular/core';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { NavigatorNode, NavigatorNodeFactory } from '@c8y/ngx-components';\nimport { TenantListGuard } from './tenant-list/tenant-list.guard';\nimport { TenantsModuleConfig, TENANTS_MODULE_CONFIG } from './tenants.model';\n\n@Injectable()\nexport class TenantsNavigationFactory implements NavigatorNodeFactory {\n private navs: NavigatorNode[] = [];\n\n constructor(\n private tenantListGuard: TenantListGuard,\n @Optional() @Inject(TENANTS_MODULE_CONFIG) private config: TenantsModuleConfig\n ) {}\n\n async get(): Promise<NavigatorNode[]> {\n const canActivateTenantList = await this.tenantListGuard.canActivate();\n if (!this.navs.length) {\n const subtenantsNavigatorNode = this.config?.subtenantsNavigatorNode ?? true;\n if (subtenantsNavigatorNode !== false) {\n this.navs.push(\n new NavigatorNode({\n parent: {\n label: gettext('Tenants'),\n icon: 'c8y-layers'\n },\n label: gettext('Subtenants'),\n icon: 'c8y-sub-tenants',\n path: 'tenants',\n routerLinkExact: false,\n priority: 4000,\n hidden: !canActivateTenantList,\n ...(subtenantsNavigatorNode === true ? {} : subtenantsNavigatorNode)\n })\n );\n }\n }\n return this.navs;\n }\n}\n","import { Component, ElementRef } from '@angular/core';\nimport { ITenant } from '@c8y/client';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n FilterChip,\n FilteringFormRendererContext,\n FormGroupComponent,\n C8yTranslatePipe,\n DatePipe\n} from '@c8y/ngx-components';\nimport { TranslateService } from '@ngx-translate/core';\nimport { FormsModule } from '@angular/forms';\nimport { BsDatepickerInputDirective, BsDatepickerDirective } from 'ngx-bootstrap/datepicker';\n\nexport interface Model {\n dateFrom: Date;\n dateTo: Date;\n}\n\n@Component({\n templateUrl: './creation-time.filtering-form-renderer.component.html',\n selector: 'c8y-creation-time-filtering-form-renderer',\n imports: [\n FormsModule,\n FormGroupComponent,\n BsDatepickerInputDirective,\n BsDatepickerDirective,\n C8yTranslatePipe\n ],\n providers: [DatePipe]\n})\nexport class CreationTimeFilteringFormRendererComponent {\n model: Model;\n\n constructor(\n public context: FilteringFormRendererContext,\n private c8yDate: DatePipe,\n private translateService: TranslateService\n ) {\n this.model = (this.context.property.externalFilterQuery || {}).model || {};\n }\n\n applyFilter() {\n this.context.applyFilter({\n externalFilterQuery: {\n model: this.model,\n chips: this.getChipsForModel(this.model)\n },\n filterPredicate: (tenant: ITenant) => {\n const creationTime = new Date(tenant.creationTime);\n let dateFrom;\n let dateTo;\n\n if (this.model.dateFrom) {\n dateFrom = this.model.dateFrom;\n dateFrom.setHours(0, 0, 0, 0);\n }\n\n if (this.model.dateTo) {\n dateTo = this.model.dateTo;\n dateTo.setHours(23, 59, 59, 999);\n }\n\n return Boolean(\n (!dateFrom && !dateTo) ||\n (dateFrom && !dateTo && dateFrom <= creationTime) ||\n (!dateFrom && dateTo && creationTime <= dateTo) ||\n (dateFrom && dateTo && dateFrom <= creationTime && creationTime <= dateTo)\n );\n }\n });\n }\n\n getChipsForModel(model: Model): FilterChip[] {\n const updateChips = externalFilterQuery => {\n externalFilterQuery.chips = this.getChipsForModel(externalFilterQuery.model);\n };\n const createChip = (key: keyof Model, displayValueTpl: string): FilterChip => {\n return {\n columnName: this.context.property.name,\n path: ['model', key],\n displayValue: this.translateService.instant(displayValueTpl, {\n date: this.c8yDate.transform(this.model[key], 'mediumDate')\n }),\n value: this.model[key],\n remove() {\n delete this.externalFilterQuery.model[key];\n updateChips(this.externalFilterQuery);\n return {\n columnName: this.columnName,\n externalFilterQuery: this.externalFilterQuery\n };\n }\n };\n };\n\n const chips = [];\n if (model.dateFrom) {\n chips.push(createChip('dateFrom', gettext('from: {{ date }}')));\n }\n if (model.dateTo) {\n chips.push(createChip('dateTo', gettext('to: {{ date }}')));\n }\n return chips;\n }\n\n resetFilter() {\n this.context.resetFilter();\n }\n\n // this method is only used for workaround for ngx-bootstrap issue (preventing the datepicker from closing dropdown when clicking inside the datepicker container)\n // TODO: remove this method when the issue is resolved https://github.com/valor-software/ngx-bootstrap/issues/6736\n datepickerShown({ _element }: { _element: ElementRef }) {\n const datepickerContainerElement = _element.nativeElement;\n datepickerContainerElement.addEventListener('click', (e: MouseEvent) => {\n e.stopPropagation();\n });\n }\n}\n","<form #filterForm=\"ngForm\">\n <div class=\"m-b-8 p-t-8\">\n <label>{{ 'Filter by creation time' | translate }}</label>\n <c8y-form-group\n class=\"datepicker d-block m-b-16\"\n style=\"max-height: 32px\"\n >\n <input\n class=\"form-control fit-w text-left\"\n placeholder=\"{{ 'Created from`date`' | translate }}\"\n name=\"dateFrom\"\n [(ngModel)]=\"model.dateFrom\"\n bsDatepicker\n [bsConfig]=\"{ customTodayClass: 'today', returnFocusToInput: true }\"\n [maxDate]=\"model.dateTo\"\n (onShown)=\"datepickerShown($event)\"\n />\n </c8y-form-group>\n <c8y-form-group\n class=\"datepicker m-l-0 d-block\"\n style=\"max-height: 32px\"\n >\n <input\n class=\"form-control fit-w text-left\"\n placeholder=\"{{ 'Created to`date`' | translate }}\"\n name=\"dateTo\"\n [(ngModel)]=\"model.dateTo\"\n bsDatepicker\n [bsConfig]=\"{ customTodayClass: 'today', returnFocusToInput: true }\"\n [minDate]=\"model.dateFrom\"\n (onShown)=\"datepickerShown($event)\"\n />\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 title=\"{{ 'Reset' | translate }}\"\n (click)=\"resetFilter()\"\n >\n {{ 'Reset' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n [disabled]=\"filterForm.invalid || !(model.dateFrom || model.dateTo)\"\n (click)=\"applyFilter()\"\n >\n {{ 'Apply' | translate }}\n </button>\n</div>\n","import { Component } from '@angular/core';\nimport { ITenant, TenantStatus } from '@c8y/client';\nimport {\n FilteringFormRendererContext,\n FormGroupComponent,\n C8yTranslatePipe\n} from '@c8y/ngx-components';\nimport { FormsModule } from '@angular/forms';\n\n@Component({\n templateUrl: './status.filtering-form-renderer.component.html',\n selector: 'c8y-status-filtering-form-renderer',\n imports: [FormsModule, FormGroupComponent, C8yTranslatePipe]\n})\nexport class StatusFilteringFormRendererComponent {\n model: {\n active: boolean;\n suspended: boolean;\n };\n\n constructor(public context: FilteringFormRendererContext) {\n this.model = (this.context.property.externalFilterQuery || {}).model || {};\n }\n\n applyFilter() {\n this.context.applyFilter({\n externalFilterQuery: {\n model: this.model\n },\n filterPredicate: (tenant: ITenant) =>\n Boolean(\n (!this.model.active && !this.model.suspended) ||\n (this.model.active && tenant.status === TenantStatus.ACTIVE) ||\n (this.model.suspended && tenant.status === TenantStatus.SUSPENDED)\n )\n });\n }\n\n resetFilter() {\n this.context.resetFilter();\n }\n}\n","<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","import { Location, NgIf, AsyncPipe } from '@angular/common';\nimport { Component, OnInit } from '@angular/core';\nimport { TranslateService } from '@ngx-translate/core';\nimport { saveAs } from 'file-saver';\nimport { BehaviorSubject, from } from 'rxjs';\nimport { expand, reduce, shareReplay, takeWhile } from 'rxjs/operators';\n\nimport {\n ICurrentTenant,\n IResultList,\n ITenant,\n TenantService,\n TenantStatus,\n UserService\n} from '@c8y/client';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n ActionControl,\n AlertService,\n AppStateService,\n BuiltInActionType,\n Column,\n DisplayOptions,\n GainsightService,\n ModalService,\n Pagination,\n PasswordService,\n Permissions,\n SortOrder,\n Status,\n TenantUiService,\n TitleComponent,\n BreadcrumbComponent,\n BreadcrumbItemComponent,\n ActionBarItemComponent,\n IconDirective,\n HelpComponent,\n DataGridComponent,\n LoadingComponent,\n EmptyStateContextDirective,\n EmptyStateComponent,\n GuideDocsComponent,\n C8yTranslateDirective,\n GuideHrefDirective,\n ColumnDirective,\n CellRendererDefDirective,\n C8yTranslatePipe,\n DatePipe\n} from '@c8y/ngx-components';\n\nimport { CreationTimeFilteringFormRendererComponent } from './creation-time.filtering-form-renderer.component';\nimport { StatusFilteringFormRendererComponent } from './status.filtering-form-renderer.component';\nimport { PRODUCT_EXPERIENCE_TENANT_MANAGEMENT } from './../tenants.model';\nimport { RouterLink } from '@angular/router';\n\nconst { EVENTS, COMPONENTS, ACTIONS } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;\n\n@Component({\n selector: 'c8y-tenant-list',\n templateUrl: './tenant-list.component.html',\n imports: [\n TitleComponent,\n BreadcrumbComponent,\n BreadcrumbItemComponent,\n NgIf,\n ActionBarItemComponent,\n IconDirective,\n HelpComponent,\n DataGridComponent,\n LoadingComponent,\n EmptyStateContextDirective,\n EmptyStateComponent,\n GuideDocsComponent,\n C8yTranslateDirective,\n GuideHrefDirective,\n ColumnDirective,\n CellRendererDefDirective,\n RouterLink,\n C8yTranslatePipe,\n AsyncPipe,\n DatePipe\n ]\n})\nexport class TenantListComponent implements OnInit {\n tenants$: BehaviorSubject<ITenant[]> = new BehaviorSubject(undefined);\n isPermittedToCreateTenanant = this.permissionsService.hasAnyRole([\n Permissions.ROLE_TENANT_MANAGEMENT_ADMIN,\n Permissions.ROLE_TENANT_MANAGEMENT_CREATE\n ]);\n currentTenant: ICurrentTenant;\n isManagementTenant: boolean;\n TOP_TENANT_NAME = 'management';\n\n title: string = null;\n loadMoreItemsLabel: string = gettext('Load more tenants');\n loadingItemsLabel: string = gettext('Loading tenants…');\n\n displayOptions: DisplayOptions = {\n bordered: false,\n striped: true,\n filter: true,\n gridHeader: true,\n hover: true\n };\n\n columns: Column[] = this.getColumns();\n pagination: Pagination = this.getPagination();\n showSearch = true;\n actionControls: ActionControl[] = this.getActionControls();\n\n noResultsMessage = gettext('No tenants to display.');\n noDataMessage = gettext('There are no tenants defined.');\n noResultsSubtitle = gettext('Refine your search terms or check your spelling.');\n noDataSubtitle = gettext('Create the first tenant.');\n\n TenantStatus = TenantStatus;\n\n constructor(\n public appState: AppStateService,\n private alertService: AlertService,\n private modalService: ModalService,\n private translateService: TranslateService,\n private tenantService: TenantService,\n private tenantUiService: TenantUiService,\n private location: Location,\n private passwordService: PasswordService,\n private userService: UserService,\n private permissionsService: Permissions,\n private gainsightService: GainsightService\n ) {}\n\n async ngOnInit() {\n this.currentTenant = this.appState.currentTenant.value;\n this.isManagementTenant = await this.tenantUiService.isManagementTenant();\n this.loadTenants();\n }\n\n loadTenants() {\n this.tenants$.next(undefined);\n from(this.tenantService.list({ pageSize: 2000, withTotalPages: true, withApps: false }))\n .pipe(\n expand(resultList => resultList.paging.nextPage !== null && resultList.paging.next()),\n takeWhile(resultList => resultList.paging.nextPage !== null, true),\n reduce(\n (tenants: ITenant[], resultList: IResultList<ITenant>) => [\n ...tenants,\n ...resultList.data\n ],\n []\n ),\n shareReplay(1)\n )\n .subscribe(tenants => this.tenants$.next(tenants));\n }\n\n getColumns(): Column[] {\n return [\n {\n name: 'company',\n header: gettext('Tenant'),\n path: 'company',\n filterable: true,\n sortable: true,\n sortOrder: 'asc' as SortOrder\n },\n {\n name: 'id',\n header: gettext('ID'),\n path: 'id',\n filterable: true,\n sortable: true\n },\n {\n name: 'domain',\n header: gettext('Domain'),\n path: 'domain',\n filterable: true,\n sortable: true\n },\n {\n name: 'parent',\n header: gettext('Parent tenant'),\n path: 'parent',\n filterable: true,\n sortable: true\n },\n {\n name: 'contactName',\n header: gettext('Contact name'),\n path: 'contactName',\n filterable: true,\n sortable: true\n },\n {\n name: 'creationTime',\n header: gettext('Created'),\n path: 'creationTime',\n filterable: true,\n filteringFormRendererComponent: CreationTimeFilteringFormRendererComponent,\n sortable: true\n },\n {\n name: 'externalReference',\n header: gettext('External reference'),\n path: 'customProperties.externalReference',\n filterable: true,\n sortable: true\n },\n {\n name: 'status',\n header: gettext('Status'),\n path: 'status',\n filterable: true,\n filteringFormRendererComponent: StatusFilteringFormRendererComponent,\n sortable: true,\n resizable: false\n }\n ];\n }\n\n getPagination(): Pagination {\n return {\n pageSize: 10,\n currentPage: 1\n };\n }\n\n getActionControls(): ActionControl[] {\n return [\n {\n type: BuiltInActionType.Edit,\n text: gettext('Edit`tenant`'),\n callback: tenant => this.goToDetails(tenant)\n },\n {\n type: 'activateTenantAction',\n icon: 'power-off',\n text: gettext('Activate`tenant`'),\n callback: async (tenant: ITenant) => {\n await this.activateTenant(tenant);\n this.loadTenants();\n },\n showIf: (tenant: ITenant) => this.isSuspended(tenant)\n },\n {\n type: 'suspendTenantAction',\n icon: 'power-off',\n text: gettext('Suspend`tenant`'),\n callback: async (tenant: ITenant) => {\n await this.suspendTenant(tenant);\n this.loadTenants();\n },\n showIf: (tenant: ITenant) => this.isActive(tenant)\n },\n {\n type: BuiltInActionType.Delete,\n text: gettext('Delete`tenant`'),\n callback: tenant => this.delete(tenant),\n showIf: () => this.isManagementTenant\n }\n ];\n }\n\n createTenant(options: { sendGainsightEvent: boolean } = { sendGainsightEvent: true }) {\n if (options.sendGainsightEvent) {\n this.gainsightService.triggerEvent(EVENTS.TENANT_MANAGEMENT, {\n component: COMPONENTS.TENANT_LIST,\n action: ACTIONS.TENANT_CREATION_INITIALIZED\n });\n }\n\n this.location.go('/tenants/new');\n }\n\n goToDetails(tenant: ITenant) {\n this.location.go(`/tenants/${tenant.id}`);\n }\n\n async activateTenant(tenant) {\n try {\n const { data: savedTenant } = await this.tenantService.update({\n id: tenant.id,\n status: TenantStatus.ACTIVE\n });\n Object.assign(tenant, savedTenant);\n this.alertService.success(gettext('Tenant activated.'));\n } catch (ex) {\n if (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n }\n\n async suspendTenant(tenant) {\n const title = gettext('Suspend tenant');\n const confirmationText = gettext(\n 'You are about to suspend tenant \"{{ company }}\" (ID \"{{ id }}\").'\n );\n const proceed = gettext('Do you want to proceed?');\n const body = [\n this.translateService.instant(confirmationText, {\n company: tenant.company,\n id: tenant.id\n }),\n this.translateService.instant(proceed)\n ].join(' ');\n const labels = {\n ok: gettext('Suspend`tenant`')\n };\n try {\n await this.modalService.confirm(title, body, Status.DANGER, labels);\n const confirmed = await this.passwordService.confirmPassword().toPromise();\n if (confirmed === true) {\n const { data: savedTenant } = await this.tenantService.update({\n id: tenant.id,\n status: TenantStatus.SUSPENDED\n });\n Object.assign(tenant, savedTenant);\n this.alertService.success(gettext('Tenant suspended.'));\n }\n } catch (ex) {\n if (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n }\n\n async delete(tenant) {\n const title = gettext('Delete tenant');\n const confirmationText = gettext(\n 'You are about to delete tenant \"{{ company }}\" (ID \"{{ id }}\").'\n );\n const hint = gettext('This operation is irreversible.');\n const proceed = gettext('Do you want to proceed?');\n const body = [\n this.translateService.instant(confirmationText, {\n company: tenant.company,\n id: tenant.id\n }),\n this.translateService.instant(hint),\n this.translateService.instant(proceed)\n ].join(' ');\n const labels = {\n ok: gettext('Delete`tenant`')\n };\n try {\n await this.modalService.confirm(title, body, Status.DANGER, labels);\n const confirmed = await this.passwordService.confirmPassword().toPromise();\n if (confirmed === true) {\n await this.tenantService.delete(tenant);\n const tenantsWithoutRemovedOne = this.tenants$.value.filter(t => t !== tenant);\n this.tenants$.next(tenantsWithoutRemovedOne);\n this.alertService.success(\n gettext('Tenant is being deleted in the background. This might take a while…')\n );\n }\n } catch (ex) {\n if (ex) {\n this.alertService.addServerFailure(ex);\n }\n }\n }\n\n isActive(tenant: ITenant) {\n return tenant.status === TenantStatus.ACTIVE;\n }\n\n isSuspended(tenant: ITenant) {\n return tenant.status === TenantStatus.SUSPENDED;\n }\n\n async downloadNewsletterEmails() {\n const { res, data } = await this.userService.getNewsletterEmails();\n const contentType = res.headers.get('content-type');\n const contentDisposition = res.headers.get('content-disposition');\n const filename = /filename=\"(.*)\"/.exec(contentDisposition)[1];\n const blob = new Blob([data], { type: contentType });\n saveAs(blob, filename);\n }\n}\n","<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","import { ValidatorFn, Validators } from '@angular/forms';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { ValidationPattern, validateInternationalPhoneNumber } from '@c8y/ngx-components';\n\n// Model for individual tenant form fields\nexport interface TenantPropertyDefinition {\n id: string;\n defaultValue: number | string | boolean | null;\n type: 'text' | 'checkbox' | 'number' | 'select';\n label: string;\n validators: ValidatorFn[];\n placeholder?: string;\n}\n\n/**\n * Define all hardTyped tenant form fields, checkboxes and select elements.\n *\n * while still benefiting from hard typing, thanks to \"satisfies\" keyword\n */\nconst tenantPropertiesDefinitions = {\n tenantID: {\n id: 'tenantId',\n validators: [],\n defaultValue: null,\n type: 'text',\n label: gettext('ID')\n },\n domain: {\n id: 'domain',\n validators: [\n Validators.required,\n Validators.maxLength(32),\n // TODO: To be consistent, pattern should be change for domain. It is tenantId, to avoid breaking tests, and be consistent with previous implementation.\n Validators.pattern(ValidationPattern.rules.tenantId.pattern)\n ],\n defaultValue: null,\n type: 'text',\n label: gettext('Domain/URL'),\n placeholder: gettext('e.g. my-tenant`used in URL`')\n },\n companyName: {\n id: 'companyName',\n validators: [\n Validators.required,\n Validators.maxLength(256),\n Validators.pattern(ValidationPattern.rules.noLeadingOrTrailingWhitespaces.pattern)\n ],\n defaultValue: null,\n type: 'text',\n label: gettext('Name'),\n placeholder: gettext('e.g. Company A')\n },\n contactName: {\n id: 'contactName',\n validators: [Validators.maxLength(30)],\n defaultValue: null,\n type: 'text',\n label: gettext('Contact name'),\n placeholder: gettext('e.g. Joe Doe`LOCALIZE`')\n },\n contactPhone: {\n id: 'contactPhone',\n validators: [Validators.required, validateInternationalPhoneNumber(), Validators.maxLength(20)],\n defaultValue: null,\n type: 'text',\n label: gettext('Contact phone'),\n placeholder: gettext('e.g. +49 9 876 543 210`LOCALIZE`')\n },\n administratorEmail: {\n id: 'administratorEmail',\n validators: [Validators.required, Validators.email, Validators.maxLength(256)],\n defaultValue: null,\n type: 'text',\n label: gettext(`Administrator's email`),\n placeholder: gettext('e.g. joe.doe@example.com`LOCALIZE`')\n },\n administratorUsername: {\n id: 'administratorUsername',\n validators: [\n Validators.required,\n Validators.pattern(ValidationPattern.rules.user.pattern),\n Validators.maxLength(256)\n ],\n defaultValue: null,\n type: 'text',\n label: gettext(`Administrator's username`),\n placeholder: gettext('e.g. joe`LOCALIZE`')\n },\n externalReference: {\n id: 'externalReference',\n validators: [],\n defaultValue: null,\n type: 'text',\n label: gettext('External reference'),\n placeholder: gettext('e.g. REF12345`reference number`')\n },\n sendPasswordResetEmail: {\n id: 'sendPasswordResetEmail',\n validators: [],\n defaultValue: true,\n type: 'checkbox',\n label: gettext('Send password reset link as email')\n },\n tenantPolicy: {\n id: 'tenantPolicy',\n validators: [],\n defaultValue: null,\n type: 'select',\n label: gettext('Tenant policy')\n },\n allowCreateTenants: {\n id: 'allowCreateTenants',\n validators: [],\n defaultValue: null,\n type: 'checkbox',\n label: gettext('Allow creation of subtenants')\n },\n gainsightEnabled: {\n id: 'gainsightEnabled',\n validators: [],\n defaultValue: false,\n type: 'checkbox',\n label: gettext('Enable Gainsight product experience tracking')\n }\n} satisfies Record<string, TenantPropertyDefinition>;\n\ntype TenantPropertiesKeys = keyof typeof tenantPropertiesDefinitions;\n\nexport const tenantFormInputsDefinitions: {\n [K in TenantPropertiesKeys]: TenantPropertyDefinition;\n} = tenantPropertiesDefinitions;\n","import { Component, Input, OnInit } from '@angular/core';\nimport { ITenant } from '@c8y/client';\nimport {\n AppStateService,\n C8yTranslatePipe,\n CommonModule,\n TenantUiService,\n Permissions\n} from '@c8y/ngx-components';\n\n@Component({\n selector: 'c8y-support-user-access',\n standalone: true,\n imports: [CommonModule, C8yTranslatePipe],\n templateUrl: './support-user-access.component.html'\n})\nexport class SupportUserAccessComponent implements OnInit {\n @Input()\n tenant: ITenant;\n\n isTopTenant: boolean;\n supportUserLogin: string;\n\n constructor(\n private tenantUiService: TenantUiService,\n private appState: AppStateService,\n private permissions: Permissions\n ) {}\n\n async ngOnInit() {\n this.isTopTenant = await this.tenantUiService.isManagementTenant();\n this.getSupportUserLogin();\n }\n\n private async getSupportUserLogin(): Promise<void> {\n const currentUser = (await this.appState.currentUser).value;\n if (\n this.permissions.hasAnyRole([Permissions.ROLE_SUPPORT_ADMIN, Permissions.ROLE_SUPPORT_READ])\n ) {\n this.supportUserLogin = encodeURIComponent(`${currentUser.id}$`);\n }\n }\n}\n","<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 @if (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/?skipSSORedirect=true&user={{\n supportUserLogin\n }}\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <i c8yIcon=\"external-link\"></i>\n {{ 'Log in' | translate }}\n </a>\n }\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 @if (!tenant.supportUser?.enabled) {\n <span title=\"{{ 'Disabled' | translate }}\">\n <i\n class=\"text-danger\"\n c8yIcon=\"ban\"\n ></i>\n {{ 'Disabled' | translate }}\n </span>\n } @else {\n <span title=\"{{ 'Enabled' | translate }}\">\n <i\n class=\"text-success\"\n c8yIcon=\"check-circle\"\n ></i>\n {{ 'Enabled' | translate }}\n </span>\n }\n </p>\n </div>\n </div>\n <div\n class=\"form-group\"\n data-cy=\"c8y-support-user-access--activeRequestCount\"\n >\n <label>{{ 'Active requests count' | translate }}</label>\n\n <p class=\"form-control-static\">\n @if (tenant.supportUser?.activeRequestCount) {\n <span>\n <span class=\"badge badge-danger\">\n {{ tenant.supportUser?.activeRequestCount }}\n </span>\n </span>\n }\n @if (!tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0) {\n <span>\n {{ 'No active requests' | translate }}\n </span>\n }\n @if (tenant.supportUser?.enabled && tenant.supportUser?.activeRequestCount === 0) {\n <span>\n {{ 'Not applicable' | translate }}\n </span>\n }\n </p>\n </div>\n @if (tenant.supportUser?.enabled) {\n <div class=\"form-group\">\n <label>{{ 'Expiry date' | translate }}</label>\n <div>\n <p class=\"form-control-static\">\n @if (tenant.supportUser?.expiryDate) {\n <span>\n {{ tenant.supportUser.expiryDate | c8yDate: 'medium' }}\n </span>\n } @else {\n <span>{{ 'No limit' | translate }}</span>\n }\n </p>\n </div>\n </div>\n }\n</div>\n","import { Component, OnInit, Optional, ViewChild } from '@angular/core';\nimport { ITenant, TenantService, ITenantPolicy } from '@c8y/client';\nimport { FormControl, FormGroup, NgForm, FormsModule, ReactiveFormsModule } from '@angular/forms';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n TenantUiService,\n AlertService,\n NewPassword,\n GainsightService,\n TitleComponent,\n BreadcrumbComponent,\n BreadcrumbItemComponent,\n C8yTranslateDirective,\n LoadingComponent,\n FormGroupComponent,\n RequiredInputPlaceholderDirective,\n NewPasswordComponent,\n C8yTranslatePipe\n} from '@c8y/ngx-components';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { assign, cloneDeep, find } from 'lodash-es';\nimport { Location, NgIf, NgFor, NgTemplateOutlet } from '@angular/common';\nimport { Ng1TenantPoliciesService } from '@c8y/ngx-components/upgrade/upgraded-services';\nimport { tenantFormInputsDefinitions } from './tenant-form-inputs-definitions';\nimport { TranslateService } from '@ngx-translate/core';\nimport {\n TenantManagementActionType,\n PRODUCT_EXPERIENCE_TENANT_MANAGEMENT,\n TenantManagementResultType\n} from '../tenants.model';\nimport { take } from 'rxjs/operators';\nimport { SupportUserAccessComponent } from '../support-user-access/support-user-access.component';\n\nconst { ACTIONS, COMPONENTS, EVENTS, RESULTS } = PRODUCT_EXPERIENCE_TENANT_MANAGEMENT;\n\n@Component({\n templateUrl: './tenant-form.component.html',\n selector: 'c8y-tenant-form',\n imports: [\n NgIf,\n TitleComponent,\n BreadcrumbComponent,\n BreadcrumbItemComponent,\n FormsModule,\n ReactiveFormsModule,\n C8yTranslateDirective,\n LoadingComponent,\n NgFor,\n NgTemplateOutlet,\n SupportUserAccessComponent,\n RouterLink,\n FormGroupComponent,\n RequiredInputPlaceholderDirective,\n NewPasswordComponent,\n C8yTranslatePipe\n ]\n})\nexport class TenantFormComponent implements OnInit {\n @ViewChild('passwordForm') passwordForm: NgForm;\n MANAGEMENT_TENANT_NAME = this.tenantUiService.MANAGEMENT;\n title: string;\n host: string;\n isTopTenant: boolean;\n isNew: boolean;\n isEnterpriseEditionTenant: boolean;\n tenant: ITenant | null = null;\n tenantPolicies: Array<ITenantPolicy>;\n tenantPolicyNone = { name: this.translateService.instant(gettext('None')) };\n tenantId: string;\n passwordStrengthEnforced: boolean;\n saveInProgress = false;\n showPasswordComponent = false;\n tenantPolicy: ITenantPolicy;\n fieldKeys: string[];\n tenantForm: FormGroup = new FormGroup({});\n fieldDefinitions = { ...tenantFormInputsDefinitions };\n initialized = false;\n\n constructor(\n @Optional() public tenantPoliciesServiceProvider: Ng1TenantPoliciesService,\n private tenantService: TenantService,\n private tenantUiService: TenantUiService,\n private location: Location,\n private alertService: AlertService,\n private activatedRoute: ActivatedRoute,\n private translateService: TranslateService,\n private gainsightService: GainsightService\n ) {}\n\n async ngOnInit(): Promise<void> {\n await this.getHost();\n this.getTenantId();\n await this.getTenant();\n await this.checkIfIsEnterpriseEdition();\n\n this.initialized = true;\n\n if (!this.isNew) {\n this.sendGainsightEvent(ACTIONS.TENANT_PROPERTIES_OPENED);\n }\n\n this.tenantForm.valueChanges.pipe(take(1)).subscribe(() => {\n this.sendGainsightEvent(\n this.isNew\n ? ACTIONS.TENANT_CREATION_STARTED_FILLING\n : ACTIONS.TENANT_PROPERTIES_STARTED_CHANGING\n );\n });\n }\n\n async onTenantPolicyChange(): Promise<void> {\n const passwordSettings = await this.tenantUiService.getPasswordStrengthSettings();\n this.tenantPolicy = find(this.tenantPolicies, {\n id: this.tenantForm.controls.tenantPolicy.value.id\n });\n const enforcedOnSystemLevel = passwordSettings.enforceStrength;\n const enforcedOnTenantLevel = passwordSettings.strengthValidity;\n const enforcedOnTenantPolicyLevel = this.tenantPoliciesServiceProvider\n ? await this.tenantPoliciesServiceProvider.doesEnforceStrongPassword(this.tenantPolicy)\n : undefined;\n\n if (this.isNew) {\n this.passwordStrengthEnforced = enforcedOnSystemLevel || enforcedOnTenantPolicyLevel;\n } else {\n this.passwordStrengthEnforced = enforcedOnSystemLevel || enforcedOnTenantLevel;\n }\n }\n\n setForm(): void {\n this.tenantForm.patchValue({\n tenantId: this.tenant.id,\n domain: this.tenant.domain,\n companyName: this.tenant.company,\n contactName: this.tenant.contactName,\n contactPhone: this.tenant.contactPhone,\n administratorEmail: this.tenant.adminEmail,\n administratorUsername: this.tenant.adminName,\n sendPasswordResetEmail: this.tenant.sendPasswordResetEmail,\n allowCreateTenants: this.tenant.allowCreateTenants,\n externalReference: this.tenant.customProperties.externalReference,\n gainsightEnabled: this.tenant.customProperties.gainsightEnabled\n });\n }\n\n getTenantId(): void {\n this.tenantId = this.activatedRoute.snapshot.parent.data.contextData.id;\n this.isNew = !this.tenantId;\n }\n\n async onSubmit(): Promise<void> {\n let tenantToBeSaved: ITenant;\n\n if (!this.isNew) {\n const formRawValue = this.tenantForm.getRawValue();\n const customProperties = {\n ...this.tenant.customProperties,\n externalReference: formRawValue.externalReference,\n gainsightEnabled: formRawValue.gainsightEnabled\n };\n tenantToBeSaved = {\n id: this.tenant.id,\n company: formRawValue.companyName,\n contactName: formRawValue.contactName,\n contactPhone: formRawValue.contactPhone,\n domain: formRawValue.domain,\n status: this.tenant.status,\n allowCreateTenants: this.tenantForm.value.allowCreateTenants,\n customProperties: customProperties\n };\n } else {\n tenantToBeSaved = {\n adminEmail: this.tenantForm.value.administratorEmail,\n adminName: this.tenantForm.value.administratorUsername,\n company: this.tenantForm.value.companyName,\n contactName: this.tenantForm.value.contactName,\n contactPhone: this.tenantForm.value.contactPhone,\n domain: this.tenantForm.value.domain,\n adminPass: this.tenantForm.value.password,\n sendPasswordResetEmail: this.tenantForm.value.sendPasswordResetEmail,\n tenantPolicyId: this.tenantForm.value.tenantPolicy?.id\n };\n }\n\n if (this.isNew && this.host) {\n tenantToBeSaved.domain = tenantToBeSaved.domain + this.host;\n }\n const t: ITenant = cloneDeep(tenantToBeSaved);\n if (t.storageLimitPerDevice === undefined) {\n t.storageLimitPerDevice = 0;\n }\n\n this.setSaveInProgress(true);\n\n try {\n let savedTenant;\n if (this.isNew) {\n savedTenant = (await this.tenantService.create(t)).data;\n if (this.tenantForm.value.allowCreateTenants) {\n assign(savedTenant, { allowCreateTenants: this.tenantForm.value.allowCreateTenants });\n await this.tenantService.update(savedTenant);\n }\n } else {\n savedTenant = (await this.tenantService.update(t)).data;\n }\n this.sendGainsightEvent(\n this.isNew ? ACTIONS.TENANT_CREATION_SAVED : ACTIONS.TENANT_PROPERTIES_SAVED,\n { result: RESULTS.SUCCESS }\n );\n this.onSave(savedTenant, this.tenantForm);\n } catch (error) {\n this.alertService.addServerFailure(error);\n this.sendGainsightEvent(\n this.isNew ? ACTIONS.TENANT_CREATION_SAVED : ACTIONS.TENANT_PROPERTIES_SAVED,\n { result: RESULTS.FAILURE, error }\n );\n } finally {\n this.setSaveInProgress(false);\n }\n }\n\n onSave(tenant: ITenant, form: FormGroup): void {\n if (this.isNew) {\n setTimeout(() => {\n this.location.go(`/tenants/${tenant.id}`);\n location.reload();\n }, 2000);\n this.alertService.success(gettext('Tenant saved.'));\n }\n assign(this.tenant, tenant);\n this.alertService.success(gettext('Tenant saved.'));\n form.markAsPristine();\n }\n\n setSaveInProgress(value: boolean): void {\n this.saveInProgress = value;\n }\n\n cancel(): void {\n this.location.go('tenants');\n }\n\n shouldDisableSave(): boolean {\n const disabled = !this.tenantForm.valid || !this.tenantForm.dirty || this.saveInProgress;\n\n if (this.showPasswordComponent) {\n if (this.passwordForm) {\n return disabled || !this.passwordForm.valid;\n } else {\n return true;\n }\n } else {\n return disabled;\n }\n }\n\n onNewPasswordChanged(newPassword: NewPassword): void {\n this.tenantForm.addControl('password', new FormControl(newPassword.password));\n }\n\n onCheckboxChange(inputField): void {\n if (inputField === 'sendPasswordResetEmail') {\n this.showPasswordComponent = !this.tenantForm.value.sendPasswordResetEmail;\n this.removePasswordControl(this.showPasswordComponent);\n }\n }\n\n private setDisabledState(): void {\n if (!this.isNew) {\n this.tenantForm.controls.tenantId.disable();\n this.tenantForm.controls.domain.disable();\n this.tenantForm.controls.administratorEmail.disable();\n this.tenantForm.controls.administratorUsername.disable();\n }\n }\n\n private async shouldShowSubtenantCheckbox(): Promise<boolean> {\n const currentTenantIsManagement = await this.tenantUiService.isManagementTenant();\n const editedTenantIsChildOfManagement =\n this.tenant && this.tenant.parent === this.MANAGEMENT_TENANT_NAME;\n return currentTenantIsManagement && (editedTenantIsChildOfManagement || this.isNew);\n }\n\n private removePasswordControl(passwordComponentShown: boolean): void {\n if (!passwordComponentShown) {\n this.tenantForm.removeControl('password');\n }\n }\n\n private async getTenant(): Promise<void> {\n if (this.tenantId && this.tenantId !== 'new') {\n const detailedTenant = (await this.tenantService.detail(this.tenantId)).data;\n this.title = detailedTenant.company;\n this.onTenant(detailedTenant);\n await this.generateForm();\n this.setForm();\n this.setDisabledState();\n } else {\n this.title = this.translateService.instant(gettext('New tenant'));\n await this.generateForm();\n await this.loadTenantPolicies();\n }\n }\n\n private async loadTenantPolicies(): Promise<void> {\n if (this.tenantPoliciesServiceProvider) {\n this.onTenantPolicies(await this.tenantPoliciesServiceProvider.list());\n }\n }\n\n private async getHost(): Promise<void> {\n const currentTenant = await this.tenantService.current();\n this.host = `.${currentTenant.data.domainName.split('.').slice(1).join('.')}`;\n }\n\n private async checkIfIsEnterpriseEdition(): Promise<void> {\n this.isEnterpriseEditionTenant = await this.tenantUiService.isEnterpriseTenant();\n }\n\n private onTenantPolicies(tenantPolicies): void {\n this.tenantPolicies = [this.tenantPolicyNone, ...tenantPolicies];\n }\n\n private onTenant(tenant: ITenant = {}): void {\n this.tenant = tenant;\n }\n\n private async generateForm(): Promise<void> {\n const shouldShowSubtenantCheckbox = await this.shouldShowSubtenantCheckbox();\n const gainsightAvailable = !!(await this.gainsightService.getGainsightKey());\n if (!shouldShowSubtenantCheckbox) {\n delete this.fieldDefinitions.allowCreateTenants;\n }\n if (!this.isNew) {\n delete this.fieldDefinitions.sendPasswordResetEmail;\n }\n if (this.isNew || (this.host && this.isEnterpriseEditionTenant)) {\n delete this.fieldDefinitions.tenantID;\n }\n if (!this.isNew && !this.host) {\n delete this.fieldDefinitions.domain;\n }\n if (this.isNew || !gainsightAvailable) {\n delete this.fieldDefinitions.gainsightEnabled;\n }\n if (this.isNew)