Showing
21 changed files
with
810 additions
and
31 deletions
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Injectable } from '@angular/core'; | |
18 | +import { HttpClient } from '@angular/common/http'; | |
19 | +import { PageLink } from '@shared/models/page/page-link'; | |
20 | +import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; | |
21 | +import { Observable } from 'rxjs'; | |
22 | +import { PageData } from '@shared/models/page/page-data'; | |
23 | +import { TenantProfile } from '@shared/models/tenant.model'; | |
24 | +import { EntityInfoData } from '@shared/models/entity.models'; | |
25 | + | |
26 | +@Injectable({ | |
27 | + providedIn: 'root' | |
28 | +}) | |
29 | +export class TenantProfileService { | |
30 | + | |
31 | + constructor( | |
32 | + private http: HttpClient | |
33 | + ) { } | |
34 | + | |
35 | + public getTenantProfiles(pageLink: PageLink, config?: RequestConfig): Observable<PageData<TenantProfile>> { | |
36 | + return this.http.get<PageData<TenantProfile>>(`/api/tenantProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); | |
37 | + } | |
38 | + | |
39 | + public getTenantProfile(tenantProfileId: string, config?: RequestConfig): Observable<TenantProfile> { | |
40 | + return this.http.get<TenantProfile>(`/api/tenantProfile/${tenantProfileId}`, defaultHttpOptionsFromConfig(config)); | |
41 | + } | |
42 | + | |
43 | + public saveTenantProfile(tenantProfile: TenantProfile, config?: RequestConfig): Observable<TenantProfile> { | |
44 | + return this.http.post<TenantProfile>('/api/tenantProfile', tenantProfile, defaultHttpOptionsFromConfig(config)); | |
45 | + } | |
46 | + | |
47 | + public deleteTenantProfile(tenantProfileId: string, config?: RequestConfig) { | |
48 | + return this.http.delete(`/api/tenantProfile/${tenantProfileId}`, defaultHttpOptionsFromConfig(config)); | |
49 | + } | |
50 | + | |
51 | + public setDefaultTenantProfile(tenantProfileId: string, config?: RequestConfig): Observable<TenantProfile> { | |
52 | + return this.http.post<TenantProfile>(`/api/tenantProfile/${tenantProfileId}/default`, defaultHttpOptionsFromConfig(config)); | |
53 | + } | |
54 | + | |
55 | + public getDefaultTenantProfileInfo(config?: RequestConfig): Observable<EntityInfoData> { | |
56 | + return this.http.get<EntityInfoData>('/api/tenantProfileInfo/default', defaultHttpOptionsFromConfig(config)); | |
57 | + } | |
58 | + | |
59 | + public getTenantProfileInfo(tenantProfileId: string, config?: RequestConfig): Observable<EntityInfoData> { | |
60 | + return this.http.get<EntityInfoData>(`/api/tenantProfileInfo/${tenantProfileId}`, defaultHttpOptionsFromConfig(config)); | |
61 | + } | |
62 | + | |
63 | + public getTenantProfileInfos(pageLink: PageLink, config?: RequestConfig): Observable<PageData<EntityInfoData>> { | |
64 | + return this.http.get<PageData<EntityInfoData>>(`/api/tenantProfileInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); | |
65 | + } | |
66 | + | |
67 | +} | ... | ... |
... | ... | @@ -249,7 +249,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor { |
249 | 249 | } else { |
250 | 250 | this.activeRequests--; |
251 | 251 | } |
252 | - if (this.activeRequests === 1) { | |
252 | + if (this.activeRequests === 1 && isLoading) { | |
253 | 253 | this.store.dispatch(new ActionLoadStart()); |
254 | 254 | } else if (this.activeRequests === 0) { |
255 | 255 | this.store.dispatch(new ActionLoadFinish()); | ... | ... |
... | ... | @@ -86,6 +86,13 @@ export class MenuService { |
86 | 86 | icon: 'supervisor_account' |
87 | 87 | }, |
88 | 88 | { |
89 | + name: 'tenant-profile.tenant-profiles', | |
90 | + type: 'link', | |
91 | + path: '/tenantProfiles', | |
92 | + icon: 'mdi:alpha-t-box', | |
93 | + isMdiIcon: true | |
94 | + }, | |
95 | + { | |
89 | 96 | name: 'widget.widget-library', |
90 | 97 | type: 'link', |
91 | 98 | path: '/widgets-bundles', |
... | ... | @@ -132,7 +139,13 @@ export class MenuService { |
132 | 139 | name: 'tenant.tenants', |
133 | 140 | icon: 'supervisor_account', |
134 | 141 | path: '/tenants' |
135 | - } | |
142 | + }, | |
143 | + { | |
144 | + name: 'tenant-profile.tenant-profiles', | |
145 | + icon: 'mdi:alpha-t-box', | |
146 | + isMdiIcon: true, | |
147 | + path: '/tenantProfiles' | |
148 | + }, | |
136 | 149 | ] |
137 | 150 | }, |
138 | 151 | { | ... | ... |
... | ... | @@ -36,9 +36,9 @@ import { MatDialog } from '@angular/material/dialog'; |
36 | 36 | import { MatPaginator } from '@angular/material/paginator'; |
37 | 37 | import { MatSort } from '@angular/material/sort'; |
38 | 38 | import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; |
39 | -import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; | |
39 | +import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; | |
40 | 40 | import { Direction, SortOrder } from '@shared/models/page/sort-order'; |
41 | -import { forkJoin, fromEvent, merge, Observable, Subscription } from 'rxjs'; | |
41 | +import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; | |
42 | 42 | import { TranslateService } from '@ngx-translate/core'; |
43 | 43 | import { BaseData, HasId } from '@shared/models/base-data'; |
44 | 44 | import { ActivatedRoute } from '@angular/router'; |
... | ... | @@ -59,6 +59,7 @@ import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/mod |
59 | 59 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; |
60 | 60 | import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; |
61 | 61 | import { isDefined, isUndefined } from '@core/utils'; |
62 | +import { HasUUID } from '../../../../shared/models/id/has-uuid'; | |
62 | 63 | |
63 | 64 | @Component({ |
64 | 65 | selector: 'tb-entities-table', |
... | ... | @@ -401,16 +402,19 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn |
401 | 402 | true |
402 | 403 | ).subscribe((result) => { |
403 | 404 | if (result) { |
404 | - const tasks: Observable<any>[] = []; | |
405 | + const tasks: Observable<HasUUID>[] = []; | |
405 | 406 | entities.forEach((entity) => { |
406 | 407 | if (this.entitiesTableConfig.deleteEnabled(entity)) { |
407 | - tasks.push(this.entitiesTableConfig.deleteEntity(entity.id)); | |
408 | + tasks.push(this.entitiesTableConfig.deleteEntity(entity.id).pipe( | |
409 | + map(() => entity.id), | |
410 | + catchError(() => of(null) | |
411 | + ))); | |
408 | 412 | } |
409 | 413 | }); |
410 | 414 | forkJoin(tasks).subscribe( |
411 | - () => { | |
415 | + (ids) => { | |
412 | 416 | this.updateData(); |
413 | - this.entitiesTableConfig.entitiesDeleted(entities.map((e) => e.id)); | |
417 | + this.entitiesTableConfig.entitiesDeleted(ids.filter(id => id !== null)); | |
414 | 418 | } |
415 | 419 | ); |
416 | 420 | } | ... | ... |
... | ... | @@ -84,6 +84,8 @@ import { UserFilterDialogComponent } from '@home/components/filter/user-filter-d |
84 | 84 | import { FilterUserInfoComponent } from './filter/filter-user-info.component'; |
85 | 85 | import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component'; |
86 | 86 | import { FilterPredicateValueComponent } from './filter/filter-predicate-value.component'; |
87 | +import { TenantProfileAutocompleteComponent } from './profile/tenant-profile-autocomplete.component'; | |
88 | +import { TenantProfileComponent } from './profile/tenant-profile.component'; | |
87 | 89 | |
88 | 90 | @NgModule({ |
89 | 91 | declarations: |
... | ... | @@ -150,7 +152,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c |
150 | 152 | UserFilterDialogComponent, |
151 | 153 | FilterUserInfoComponent, |
152 | 154 | FilterUserInfoDialogComponent, |
153 | - FilterPredicateValueComponent | |
155 | + FilterPredicateValueComponent, | |
156 | + TenantProfileAutocompleteComponent, | |
157 | + TenantProfileComponent | |
154 | 158 | ], |
155 | 159 | imports: [ |
156 | 160 | CommonModule, |
... | ... | @@ -206,7 +210,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c |
206 | 210 | FiltersDialogComponent, |
207 | 211 | FilterSelectComponent, |
208 | 212 | FiltersEditComponent, |
209 | - UserFilterDialogComponent | |
213 | + UserFilterDialogComponent, | |
214 | + TenantProfileAutocompleteComponent, | |
215 | + TenantProfileComponent | |
210 | 216 | ], |
211 | 217 | providers: [ |
212 | 218 | WidgetComponentService, | ... | ... |
ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<mat-form-field [formGroup]="selectTenantProfileFormGroup" class="mat-block"> | |
19 | + <input matInput type="text" placeholder="{{ 'tenant-profile.tenant-profile' | translate }}" | |
20 | + #tenantProfileInput | |
21 | + formControlName="tenantProfile" | |
22 | + [required]="required" | |
23 | + [matAutocomplete]="tenantProfileAutocomplete"> | |
24 | + <button *ngIf="selectTenantProfileFormGroup.get('tenantProfile').value && !disabled" | |
25 | + type="button" | |
26 | + matSuffix mat-button mat-icon-button aria-label="Clear" | |
27 | + (click)="clear()"> | |
28 | + <mat-icon class="material-icons">close</mat-icon> | |
29 | + </button> | |
30 | + <mat-autocomplete | |
31 | + class="tb-autocomplete" | |
32 | + #tenantProfileAutocomplete="matAutocomplete" | |
33 | + [displayWith]="displayTenantProfileFn"> | |
34 | + <mat-option *ngFor="let tenantProfile of filteredTenantProfiles | async" [value]="tenantProfile"> | |
35 | + <span [innerHTML]="tenantProfile.name | highlight:searchText"></span> | |
36 | + </mat-option> | |
37 | + <mat-option *ngIf="!(filteredTenantProfiles | async)?.length" [value]="null"> | |
38 | + <span> | |
39 | + {{ translate.get('tenant-profile.no-tenant-profiles-matching', {entity: searchText}) | async }} | |
40 | + </span> | |
41 | + </mat-option> | |
42 | + </mat-autocomplete> | |
43 | + <mat-error *ngIf="selectTenantProfileFormGroup.get('tenantProfile').hasError('required')"> | |
44 | + {{ 'tenant-profile.tenant-profile-required' | translate }} | |
45 | + </mat-error> | |
46 | +</mat-form-field> | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; | |
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; | |
19 | +import { Observable } from 'rxjs'; | |
20 | +import { PageLink } from '@shared/models/page/page-link'; | |
21 | +import { Direction } from '@shared/models/page/sort-order'; | |
22 | +import { map, mergeMap, startWith, tap } from 'rxjs/operators'; | |
23 | +import { Store } from '@ngrx/store'; | |
24 | +import { AppState } from '@app/core/core.state'; | |
25 | +import { TranslateService } from '@ngx-translate/core'; | |
26 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | |
27 | +import { TenantProfileId } from '@shared/models/id/tenant-profile-id'; | |
28 | +import { EntityInfoData } from '@shared/models/entity.models'; | |
29 | +import { TenantProfileService } from '@core/http/tenant-profile.service'; | |
30 | +import { entityIdEquals } from '../../../../shared/models/id/entity-id'; | |
31 | + | |
32 | +@Component({ | |
33 | + selector: 'tb-tenant-profile-autocomplete', | |
34 | + templateUrl: './tenant-profile-autocomplete.component.html', | |
35 | + styleUrls: [], | |
36 | + providers: [{ | |
37 | + provide: NG_VALUE_ACCESSOR, | |
38 | + useExisting: forwardRef(() => TenantProfileAutocompleteComponent), | |
39 | + multi: true | |
40 | + }] | |
41 | +}) | |
42 | +export class TenantProfileAutocompleteComponent implements ControlValueAccessor, OnInit { | |
43 | + | |
44 | + selectTenantProfileFormGroup: FormGroup; | |
45 | + | |
46 | + modelValue: TenantProfileId | null; | |
47 | + | |
48 | + @Input() | |
49 | + selectDefaultProfile = false; | |
50 | + | |
51 | + private requiredValue: boolean; | |
52 | + get required(): boolean { | |
53 | + return this.requiredValue; | |
54 | + } | |
55 | + @Input() | |
56 | + set required(value: boolean) { | |
57 | + this.requiredValue = coerceBooleanProperty(value); | |
58 | + } | |
59 | + | |
60 | + @Input() | |
61 | + disabled: boolean; | |
62 | + | |
63 | + @ViewChild('tenantProfileInput', {static: true}) tenantProfileInput: ElementRef; | |
64 | + | |
65 | + filteredTenantProfiles: Observable<Array<EntityInfoData>>; | |
66 | + | |
67 | + searchText = ''; | |
68 | + | |
69 | + private propagateChange = (v: any) => { }; | |
70 | + | |
71 | + constructor(private store: Store<AppState>, | |
72 | + public translate: TranslateService, | |
73 | + private tenantProfileService: TenantProfileService, | |
74 | + private fb: FormBuilder) { | |
75 | + this.selectTenantProfileFormGroup = this.fb.group({ | |
76 | + tenantProfile: [null] | |
77 | + }); | |
78 | + } | |
79 | + | |
80 | + registerOnChange(fn: any): void { | |
81 | + this.propagateChange = fn; | |
82 | + } | |
83 | + | |
84 | + registerOnTouched(fn: any): void { | |
85 | + } | |
86 | + | |
87 | + ngOnInit() { | |
88 | + this.filteredTenantProfiles = this.selectTenantProfileFormGroup.get('tenantProfile').valueChanges | |
89 | + .pipe( | |
90 | + tap((value: EntityInfoData | string) => { | |
91 | + let modelValue: TenantProfileId | null; | |
92 | + if (typeof value === 'string' || !value) { | |
93 | + modelValue = null; | |
94 | + } else { | |
95 | + modelValue = new TenantProfileId(value.id.id); | |
96 | + } | |
97 | + this.updateView(modelValue); | |
98 | + }), | |
99 | + startWith<string | EntityInfoData>(''), | |
100 | + map(value => value ? (typeof value === 'string' ? value : value.name) : ''), | |
101 | + mergeMap(name => this.fetchTenantProfiles(name) ) | |
102 | + ); | |
103 | + } | |
104 | + | |
105 | + selectDefaultTenantProfileIfNeeded(): void { | |
106 | + if (this.selectDefaultProfile && !this.modelValue) { | |
107 | + this.tenantProfileService.getDefaultTenantProfileInfo().subscribe( | |
108 | + (profile) => { | |
109 | + if (profile) { | |
110 | + this.modelValue = new TenantProfileId(profile.id.id); | |
111 | + this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(profile, {emitEvent: false}); | |
112 | + this.propagateChange(this.modelValue); | |
113 | + } | |
114 | + } | |
115 | + ); | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + setDisabledState(isDisabled: boolean): void { | |
120 | + this.disabled = isDisabled; | |
121 | + } | |
122 | + | |
123 | + writeValue(value: TenantProfileId | null): void { | |
124 | + this.searchText = ''; | |
125 | + if (value != null) { | |
126 | + this.tenantProfileService.getTenantProfileInfo(value.id).subscribe( | |
127 | + (profile) => { | |
128 | + this.modelValue = new TenantProfileId(profile.id.id); | |
129 | + this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(profile, {emitEvent: true}); | |
130 | + } | |
131 | + ); | |
132 | + } else { | |
133 | + this.modelValue = null; | |
134 | + this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(null, {emitEvent: true}); | |
135 | + this.selectDefaultTenantProfileIfNeeded(); | |
136 | + } | |
137 | + } | |
138 | + | |
139 | + updateView(value: TenantProfileId | null) { | |
140 | + if (!entityIdEquals(this.modelValue, value)) { | |
141 | + this.modelValue = value; | |
142 | + this.propagateChange(this.modelValue); | |
143 | + } | |
144 | + } | |
145 | + | |
146 | + displayTenantProfileFn(profile?: EntityInfoData): string | undefined { | |
147 | + return profile ? profile.name : undefined; | |
148 | + } | |
149 | + | |
150 | + fetchTenantProfiles(searchText?: string): Observable<Array<EntityInfoData>> { | |
151 | + this.searchText = searchText; | |
152 | + const pageLink = new PageLink(10, 0, searchText, { | |
153 | + property: 'name', | |
154 | + direction: Direction.ASC | |
155 | + }); | |
156 | + return this.tenantProfileService.getTenantProfileInfos(pageLink, {ignoreLoading: true}).pipe( | |
157 | + map(pageData => { | |
158 | + return pageData.data; | |
159 | + }) | |
160 | + ); | |
161 | + } | |
162 | + | |
163 | + clear() { | |
164 | + this.selectTenantProfileFormGroup.get('tenantProfile').patchValue(null, {emitEvent: true}); | |
165 | + setTimeout(() => { | |
166 | + this.tenantProfileInput.nativeElement.blur(); | |
167 | + this.tenantProfileInput.nativeElement.focus(); | |
168 | + }, 0); | |
169 | + } | |
170 | + | |
171 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div class="tb-details-buttons" fxLayout.xs="column" *ngIf="!standalone"> | |
19 | + <button mat-raised-button color="primary" | |
20 | + [disabled]="(isLoading$ | async)" | |
21 | + (click)="onEntityAction($event, 'setDefault')" | |
22 | + [fxShow]="!isEdit && !entity?.default"> | |
23 | + {{'tenant-profile.set-default' | translate }} | |
24 | + </button> | |
25 | + <button mat-raised-button color="primary" | |
26 | + [disabled]="(isLoading$ | async)" | |
27 | + (click)="onEntityAction($event, 'delete')" | |
28 | + [fxShow]="!hideDelete() && !isEdit"> | |
29 | + {{'tenant-profile.delete' | translate }} | |
30 | + </button> | |
31 | + <div fxLayout="row" fxLayout.xs="column"> | |
32 | + <button mat-raised-button | |
33 | + ngxClipboard | |
34 | + (cbOnSuccess)="onTenantProfileIdCopied($event)" | |
35 | + [cbContent]="entity?.id?.id" | |
36 | + [fxShow]="!isEdit"> | |
37 | + <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> | |
38 | + <span translate>tenant-profile.copyId</span> | |
39 | + </button> | |
40 | + </div> | |
41 | +</div> | |
42 | +<div class="mat-padding" fxLayout="column"> | |
43 | + <form [formGroup]="entityForm"> | |
44 | + <fieldset [disabled]="(isLoading$ | async) || !isEdit"> | |
45 | + <mat-form-field class="mat-block"> | |
46 | + <mat-label translate>tenant-profile.name</mat-label> | |
47 | + <input matInput formControlName="name" required/> | |
48 | + <mat-error *ngIf="entityForm.get('name').hasError('required')"> | |
49 | + {{ 'tenant-profile.name-required' | translate }} | |
50 | + </mat-error> | |
51 | + </mat-form-field> | |
52 | + <div fxLayout="column"> | |
53 | + <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore"> | |
54 | + <div>{{ 'tenant.isolated-tb-core' | translate }}</div> | |
55 | + <div class="tb-hint">{{'tenant.isolated-tb-core-details' | translate}}</div> | |
56 | + </mat-checkbox> | |
57 | + <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbRuleEngine"> | |
58 | + <div>{{ 'tenant.isolated-tb-rule-engine' | translate }}</div> | |
59 | + <div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div> | |
60 | + </mat-checkbox> | |
61 | + </div> | |
62 | + <mat-form-field class="mat-block"> | |
63 | + <mat-label translate>tenant-profile.description</mat-label> | |
64 | + <textarea matInput formControlName="description" rows="2"></textarea> | |
65 | + </mat-form-field> | |
66 | + </fieldset> | |
67 | + </form> | |
68 | +</div> | ... | ... |
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss
renamed from
ui-ngx/src/app/modules/home/pages/tenant/tenant.component.scss
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Component, Inject, Input } from '@angular/core'; | |
18 | +import { Store } from '@ngrx/store'; | |
19 | +import { AppState } from '@core/core.state'; | |
20 | +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | |
21 | +import { TenantProfile } from '@app/shared/models/tenant.model'; | |
22 | +import { ActionNotificationShow } from '@app/core/notification/notification.actions'; | |
23 | +import { TranslateService } from '@ngx-translate/core'; | |
24 | +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; | |
25 | +import { EntityComponent } from '../entity/entity.component'; | |
26 | + | |
27 | +@Component({ | |
28 | + selector: 'tb-tenant-profile', | |
29 | + templateUrl: './tenant-profile.component.html', | |
30 | + styleUrls: ['./tenant-profile.component.scss'] | |
31 | +}) | |
32 | +export class TenantProfileComponent extends EntityComponent<TenantProfile> { | |
33 | + | |
34 | + @Input() | |
35 | + standalone = false; | |
36 | + | |
37 | + constructor(protected store: Store<AppState>, | |
38 | + protected translate: TranslateService, | |
39 | + @Inject('entity') protected entityValue: TenantProfile, | |
40 | + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<TenantProfile>, | |
41 | + protected fb: FormBuilder) { | |
42 | + super(store, fb, entityValue, entitiesTableConfigValue); | |
43 | + } | |
44 | + | |
45 | + hideDelete() { | |
46 | + if (this.entitiesTableConfig) { | |
47 | + return !this.entitiesTableConfig.deleteEnabled(this.entity); | |
48 | + } else { | |
49 | + return false; | |
50 | + } | |
51 | + } | |
52 | + | |
53 | + buildForm(entity: TenantProfile): FormGroup { | |
54 | + return this.fb.group( | |
55 | + { | |
56 | + name: [entity ? entity.name : '', [Validators.required]], | |
57 | + isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], | |
58 | + isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], | |
59 | + description: [entity ? entity.description : '', []], | |
60 | + } | |
61 | + ); | |
62 | + } | |
63 | + | |
64 | + updateForm(entity: TenantProfile) { | |
65 | + this.entityForm.patchValue({name: entity.name}); | |
66 | + this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); | |
67 | + this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); | |
68 | + this.entityForm.patchValue({description: entity.description}); | |
69 | + } | |
70 | + | |
71 | + updateFormState() { | |
72 | + if (this.entityForm) { | |
73 | + if (this.isEditValue) { | |
74 | + this.entityForm.enable({emitEvent: false}); | |
75 | + if (!this.isAdd) { | |
76 | + this.entityForm.get('isolatedTbCore').disable({emitEvent: false}); | |
77 | + this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false}); | |
78 | + } | |
79 | + } else { | |
80 | + this.entityForm.disable({emitEvent: false}); | |
81 | + } | |
82 | + } | |
83 | + } | |
84 | + | |
85 | + onTenantProfileIdCopied(event) { | |
86 | + this.store.dispatch(new ActionNotificationShow( | |
87 | + { | |
88 | + message: this.translate.instant('tenant-profile.idCopiedMessage'), | |
89 | + type: 'success', | |
90 | + duration: 750, | |
91 | + verticalPosition: 'bottom', | |
92 | + horizontalPosition: 'right' | |
93 | + })); | |
94 | + } | |
95 | + | |
96 | +} | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import { EntityViewModule } from '@modules/home/pages/entity-view/entity-view.mo |
29 | 29 | import { RuleChainModule } from '@modules/home/pages/rulechain/rulechain.module'; |
30 | 30 | import { WidgetLibraryModule } from '@modules/home/pages/widget/widget-library.module'; |
31 | 31 | import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module'; |
32 | +import { TenantProfileModule } from './tenant-profile/tenant-profile.module'; | |
32 | 33 | import { MODULES_MAP } from '@shared/public-api'; |
33 | 34 | import { modulesMap } from '../../common/modules-map'; |
34 | 35 | |
... | ... | @@ -37,6 +38,7 @@ import { modulesMap } from '../../common/modules-map'; |
37 | 38 | AdminModule, |
38 | 39 | HomeLinksModule, |
39 | 40 | ProfileModule, |
41 | + TenantProfileModule, | |
40 | 42 | TenantModule, |
41 | 43 | DeviceModule, |
42 | 44 | AssetModule, | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { NgModule } from '@angular/core'; | |
18 | +import { RouterModule, Routes } from '@angular/router'; | |
19 | + | |
20 | +import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; | |
21 | +import { Authority } from '@shared/models/authority.enum'; | |
22 | +import { TenantProfilesTableConfigResolver } from './tenant-profiles-table-config.resolver'; | |
23 | + | |
24 | +const routes: Routes = [ | |
25 | + { | |
26 | + path: 'tenantProfiles', | |
27 | + data: { | |
28 | + breadcrumb: { | |
29 | + label: 'tenant-profile.tenant-profiles', | |
30 | + icon: 'mdi:alpha-t-box' | |
31 | + } | |
32 | + }, | |
33 | + children: [ | |
34 | + { | |
35 | + path: '', | |
36 | + component: EntitiesTableComponent, | |
37 | + data: { | |
38 | + auth: [Authority.SYS_ADMIN], | |
39 | + title: 'tenant-profile.tenant-profiles' | |
40 | + }, | |
41 | + resolve: { | |
42 | + entitiesTableConfig: TenantProfilesTableConfigResolver | |
43 | + } | |
44 | + } | |
45 | + ] | |
46 | + } | |
47 | +]; | |
48 | + | |
49 | +@NgModule({ | |
50 | + imports: [RouterModule.forChild(routes)], | |
51 | + exports: [RouterModule], | |
52 | + providers: [ | |
53 | + TenantProfilesTableConfigResolver | |
54 | + ] | |
55 | +}) | |
56 | +export class TenantProfileRoutingModule { } | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<mat-tab *ngIf="entity" | |
19 | + label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
22 | + [entityId]="entity.id" | |
23 | + [entityName]="entity.name"> | |
24 | + </tb-attribute-table> | |
25 | +</mat-tab> | |
26 | +<mat-tab *ngIf="entity" | |
27 | + label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
31 | + [entityId]="entity.id" | |
32 | + [entityName]="entity.name"> | |
33 | + </tb-attribute-table> | |
34 | +</mat-tab> | |
35 | +<mat-tab *ngIf="entity" | |
36 | + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab"> | |
37 | + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table> | |
38 | +</mat-tab> | |
39 | +<mat-tab *ngIf="entity" | |
40 | + label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" | |
42 | + [entityId]="entity.id"></tb-event-table> | |
43 | +</mat-tab> | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Component } from '@angular/core'; | |
18 | +import { Store } from '@ngrx/store'; | |
19 | +import { AppState } from '@core/core.state'; | |
20 | +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; | |
21 | +import { TenantProfile } from '@shared/models/tenant.model'; | |
22 | + | |
23 | +@Component({ | |
24 | + selector: 'tb-tenant-profile-tabs', | |
25 | + templateUrl: './tenant-profile-tabs.component.html', | |
26 | + styleUrls: [] | |
27 | +}) | |
28 | +export class TenantProfileTabsComponent extends EntityTabsComponent<TenantProfile> { | |
29 | + | |
30 | + constructor(protected store: Store<AppState>) { | |
31 | + super(store); | |
32 | + } | |
33 | + | |
34 | + ngOnInit() { | |
35 | + super.ngOnInit(); | |
36 | + } | |
37 | + | |
38 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { NgModule } from '@angular/core'; | |
18 | +import { CommonModule } from '@angular/common'; | |
19 | +import { SharedModule } from '@shared/shared.module'; | |
20 | +import { HomeComponentsModule } from '@modules/home/components/home-components.module'; | |
21 | +import { TenantProfileRoutingModule } from './tenant-profile-routing.module'; | |
22 | +import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; | |
23 | + | |
24 | +@NgModule({ | |
25 | + declarations: [ | |
26 | + TenantProfileTabsComponent | |
27 | + ], | |
28 | + imports: [ | |
29 | + CommonModule, | |
30 | + SharedModule, | |
31 | + HomeComponentsModule, | |
32 | + TenantProfileRoutingModule | |
33 | + ] | |
34 | +}) | |
35 | +export class TenantProfileModule { } | ... | ... |
ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts
0 → 100644
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Injectable } from '@angular/core'; | |
18 | +import { Resolve } from '@angular/router'; | |
19 | +import { TenantProfile } from '@shared/models/tenant.model'; | |
20 | +import { | |
21 | + checkBoxCell, | |
22 | + DateEntityTableColumn, | |
23 | + EntityTableColumn, | |
24 | + EntityTableConfig | |
25 | +} from '@home/models/entity/entities-table-config.models'; | |
26 | +import { TranslateService } from '@ngx-translate/core'; | |
27 | +import { DatePipe } from '@angular/common'; | |
28 | +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; | |
29 | +import { EntityAction } from '@home/models/entity/entity-component.models'; | |
30 | +import { TenantProfileService } from '@core/http/tenant-profile.service'; | |
31 | +import { TenantProfileComponent } from '../../components/profile/tenant-profile.component'; | |
32 | +import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; | |
33 | +import { DialogService } from '@core/services/dialog.service'; | |
34 | + | |
35 | +@Injectable() | |
36 | +export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> { | |
37 | + | |
38 | + private readonly config: EntityTableConfig<TenantProfile> = new EntityTableConfig<TenantProfile>(); | |
39 | + | |
40 | + constructor(private tenantProfileService: TenantProfileService, | |
41 | + private translate: TranslateService, | |
42 | + private datePipe: DatePipe, | |
43 | + private dialogService: DialogService) { | |
44 | + | |
45 | + this.config.entityType = EntityType.TENANT_PROFILE; | |
46 | + this.config.entityComponent = TenantProfileComponent; | |
47 | + this.config.entityTabsComponent = TenantProfileTabsComponent; | |
48 | + this.config.entityTranslations = entityTypeTranslations.get(EntityType.TENANT_PROFILE); | |
49 | + this.config.entityResources = entityTypeResources.get(EntityType.TENANT_PROFILE); | |
50 | + | |
51 | + this.config.columns.push( | |
52 | + new DateEntityTableColumn<TenantProfile>('createdTime', 'common.created-time', this.datePipe, '150px'), | |
53 | + new EntityTableColumn<TenantProfile>('name', 'tenant-profile.name', '40%'), | |
54 | + new EntityTableColumn<TenantProfile>('description', 'tenant-profile.description', '60%'), | |
55 | + new EntityTableColumn<TenantProfile>('isDefault', 'tenant-profile.default', '60px', | |
56 | + entity => { | |
57 | + return checkBoxCell(entity.default); | |
58 | + }) | |
59 | + ); | |
60 | + | |
61 | + this.config.cellActionDescriptors.push( | |
62 | + { | |
63 | + name: this.translate.instant('tenant-profile.set-default'), | |
64 | + icon: 'flag', | |
65 | + isEnabled: (tenantProfile) => !tenantProfile.default, | |
66 | + onAction: ($event, entity) => this.setDefaultTenantProfile($event, entity) | |
67 | + } | |
68 | + ); | |
69 | + | |
70 | + this.config.deleteEntityTitle = tenantProfile => this.translate.instant('tenant-profile.delete-tenant-profile-title', | |
71 | + { tenantProfileName: tenantProfile.name }); | |
72 | + this.config.deleteEntityContent = () => this.translate.instant('tenant-profile.delete-tenant-profile-text'); | |
73 | + this.config.deleteEntitiesTitle = count => this.translate.instant('tenant-profile.delete-tenant-profiles-title', {count}); | |
74 | + this.config.deleteEntitiesContent = () => this.translate.instant('tenant-profile.delete-tenant-profiles-text'); | |
75 | + | |
76 | + this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink); | |
77 | + this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id); | |
78 | + this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile); | |
79 | + this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id); | |
80 | + this.config.onEntityAction = action => this.onTenantProfileAction(action); | |
81 | + this.config.deleteEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default; | |
82 | + this.config.entitySelectionEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default; | |
83 | + } | |
84 | + | |
85 | + resolve(): EntityTableConfig<TenantProfile> { | |
86 | + this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles'); | |
87 | + | |
88 | + return this.config; | |
89 | + } | |
90 | + | |
91 | + setDefaultTenantProfile($event: Event, tenantProfile: TenantProfile) { | |
92 | + if ($event) { | |
93 | + $event.stopPropagation(); | |
94 | + } | |
95 | + this.dialogService.confirm( | |
96 | + this.translate.instant('tenant-profile.set-default-tenant-profile-title', {tenantProfileName: tenantProfile.name}), | |
97 | + this.translate.instant('tenant-profile.set-default-tenant-profile-text'), | |
98 | + this.translate.instant('action.no'), | |
99 | + this.translate.instant('action.yes'), | |
100 | + true | |
101 | + ).subscribe((res) => { | |
102 | + if (res) { | |
103 | + this.tenantProfileService.setDefaultTenantProfile(tenantProfile.id.id).subscribe( | |
104 | + () => { | |
105 | + this.config.table.updateData(); | |
106 | + } | |
107 | + ); | |
108 | + } | |
109 | + } | |
110 | + ); | |
111 | + } | |
112 | + | |
113 | + onTenantProfileAction(action: EntityAction<TenantProfile>): boolean { | |
114 | + switch (action.action) { | |
115 | + case 'setDefault': | |
116 | + this.setDefaultTenantProfile(action.event, action.entity); | |
117 | + return true; | |
118 | + } | |
119 | + return false; | |
120 | + } | |
121 | + | |
122 | +} | ... | ... |
... | ... | @@ -49,6 +49,11 @@ |
49 | 49 | {{ 'tenant.title-required' | translate }} |
50 | 50 | </mat-error> |
51 | 51 | </mat-form-field> |
52 | + <tb-tenant-profile-autocomplete | |
53 | + [selectDefaultProfile]="isAdd" | |
54 | + required | |
55 | + formControlName="tenantProfileId"> | |
56 | + </tb-tenant-profile-autocomplete> | |
52 | 57 | <div formGroupName="additionalInfo" fxLayout="column"> |
53 | 58 | <mat-form-field class="mat-block"> |
54 | 59 | <mat-label translate>tenant.description</mat-label> |
... | ... | @@ -56,16 +61,6 @@ |
56 | 61 | </mat-form-field> |
57 | 62 | </div> |
58 | 63 | <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact> |
59 | - <!--div fxLayout="column"> | |
60 | - <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore"> | |
61 | - <div>{{ 'tenant.isolated-tb-core' | translate }}</div> | |
62 | - <div class="tb-hint">{{'tenant.isolated-tb-core-details' | translate}}</div> | |
63 | - </mat-checkbox> | |
64 | - <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbRuleEngine"> | |
65 | - <div>{{ 'tenant.isolated-tb-rule-engine' | translate }}</div> | |
66 | - <div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div> | |
67 | - </mat-checkbox> | |
68 | - </div--> | |
69 | 64 | </fieldset> |
70 | 65 | </form> |
71 | 66 | </div> | ... | ... |
... | ... | @@ -27,7 +27,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod |
27 | 27 | @Component({ |
28 | 28 | selector: 'tb-tenant', |
29 | 29 | templateUrl: './tenant.component.html', |
30 | - styleUrls: ['./tenant.component.scss'] | |
30 | + styleUrls: [] | |
31 | 31 | }) |
32 | 32 | export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
33 | 33 | |
... | ... | @@ -52,8 +52,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
52 | 52 | { |
53 | 53 | title: [entity ? entity.title : '', [Validators.required]], |
54 | 54 | tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], |
55 | - // isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], | |
56 | - // isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], | |
57 | 55 | additionalInfo: this.fb.group( |
58 | 56 | { |
59 | 57 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] |
... | ... | @@ -66,8 +64,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
66 | 64 | updateEntityForm(entity: Tenant) { |
67 | 65 | this.entityForm.patchValue({title: entity.title}); |
68 | 66 | this.entityForm.patchValue({tenantProfileId: entity.tenantProfileId}); |
69 | - // this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); | |
70 | - // this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); | |
71 | 67 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); |
72 | 68 | } |
73 | 69 | |
... | ... | @@ -75,10 +71,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
75 | 71 | if (this.entityForm) { |
76 | 72 | if (this.isEditValue) { |
77 | 73 | this.entityForm.enable({emitEvent: false}); |
78 | - /* if (!this.isAdd) { | |
79 | - this.entityForm.get('isolatedTbCore').disable({emitEvent: false}); | |
80 | - this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false}); | |
81 | - } */ | |
82 | 74 | } else { |
83 | 75 | this.entityForm.disable({emitEvent: false}); |
84 | 76 | } | ... | ... |
... | ... | @@ -16,7 +16,16 @@ |
16 | 16 | |
17 | 17 | import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; |
18 | 18 | import { HasUUID } from '@shared/models/id/has-uuid'; |
19 | +import { isDefinedAndNotNull } from '@core/utils'; | |
19 | 20 | |
20 | 21 | export interface EntityId extends HasUUID { |
21 | 22 | entityType: EntityType | AliasEntityType; |
22 | 23 | } |
24 | + | |
25 | +export function entityIdEquals(entityId1: EntityId, entityId2: EntityId): boolean { | |
26 | + if (isDefinedAndNotNull(entityId1) && isDefinedAndNotNull(entityId2)) { | |
27 | + return entityId1.id === entityId2.id && entityId1.entityType === entityId2.entityType; | |
28 | + } else { | |
29 | + return entityId1 === entityId2; | |
30 | + } | |
31 | +} | ... | ... |
... | ... | @@ -26,7 +26,7 @@ export interface TenantProfileData { |
26 | 26 | export interface TenantProfile extends BaseData<TenantProfileId> { |
27 | 27 | name: string; |
28 | 28 | description: string; |
29 | - isDefault: boolean; | |
29 | + default: boolean; | |
30 | 30 | isolatedTbCore: boolean; |
31 | 31 | isolatedTbRuleEngine: boolean; |
32 | 32 | profileData: TenantProfileData; | ... | ... |
... | ... | @@ -1683,7 +1683,23 @@ |
1683 | 1683 | "tenant-profile-details": "Tenant profile details", |
1684 | 1684 | "no-tenant-profiles-text": "No tenant profiles found", |
1685 | 1685 | "search": "Search tenant profiles", |
1686 | - "selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected" | |
1686 | + "selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected", | |
1687 | + "no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.", | |
1688 | + "tenant-profile-required": "Tenant profile is required", | |
1689 | + "idCopiedMessage": "Tenant profile Id has been copied to clipboard", | |
1690 | + "set-default": "Make tenant profile default", | |
1691 | + "delete": "Delete tenant profile", | |
1692 | + "copyId": "Copy tenant profile Id", | |
1693 | + "name": "Name", | |
1694 | + "name-required": "Name is required.", | |
1695 | + "description": "Description", | |
1696 | + "default": "Default", | |
1697 | + "delete-tenant-profile-title": "Are you sure you want to delete the tenant profile '{{tenantProfileName}}'?", | |
1698 | + "delete-tenant-profile-text": "Be careful, after the confirmation the tenant profile and all related data will become unrecoverable.", | |
1699 | + "delete-tenant-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 tenant profile} other {# tenant profiles} }?", | |
1700 | + "delete-tenant-profiles-text": "Be careful, after the confirmation all selected tenant profiles will be removed and all related data will become unrecoverable.", | |
1701 | + "set-default-tenant-profile-title": "Are you sure you want to make the tenant profile '{{tenantProfileName}}' root?", | |
1702 | + "set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified." | |
1687 | 1703 | }, |
1688 | 1704 | "timeinterval": { |
1689 | 1705 | "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", | ... | ... |