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,7 +249,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor { | ||
249 | } else { | 249 | } else { |
250 | this.activeRequests--; | 250 | this.activeRequests--; |
251 | } | 251 | } |
252 | - if (this.activeRequests === 1) { | 252 | + if (this.activeRequests === 1 && isLoading) { |
253 | this.store.dispatch(new ActionLoadStart()); | 253 | this.store.dispatch(new ActionLoadStart()); |
254 | } else if (this.activeRequests === 0) { | 254 | } else if (this.activeRequests === 0) { |
255 | this.store.dispatch(new ActionLoadFinish()); | 255 | this.store.dispatch(new ActionLoadFinish()); |
@@ -86,6 +86,13 @@ export class MenuService { | @@ -86,6 +86,13 @@ export class MenuService { | ||
86 | icon: 'supervisor_account' | 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 | name: 'widget.widget-library', | 96 | name: 'widget.widget-library', |
90 | type: 'link', | 97 | type: 'link', |
91 | path: '/widgets-bundles', | 98 | path: '/widgets-bundles', |
@@ -132,7 +139,13 @@ export class MenuService { | @@ -132,7 +139,13 @@ export class MenuService { | ||
132 | name: 'tenant.tenants', | 139 | name: 'tenant.tenants', |
133 | icon: 'supervisor_account', | 140 | icon: 'supervisor_account', |
134 | path: '/tenants' | 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,9 +36,9 @@ import { MatDialog } from '@angular/material/dialog'; | ||
36 | import { MatPaginator } from '@angular/material/paginator'; | 36 | import { MatPaginator } from '@angular/material/paginator'; |
37 | import { MatSort } from '@angular/material/sort'; | 37 | import { MatSort } from '@angular/material/sort'; |
38 | import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; | 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 | import { Direction, SortOrder } from '@shared/models/page/sort-order'; | 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 | import { TranslateService } from '@ngx-translate/core'; | 42 | import { TranslateService } from '@ngx-translate/core'; |
43 | import { BaseData, HasId } from '@shared/models/base-data'; | 43 | import { BaseData, HasId } from '@shared/models/base-data'; |
44 | import { ActivatedRoute } from '@angular/router'; | 44 | import { ActivatedRoute } from '@angular/router'; |
@@ -59,6 +59,7 @@ import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/mod | @@ -59,6 +59,7 @@ import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/mod | ||
59 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; | 59 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; |
60 | import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; | 60 | import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; |
61 | import { isDefined, isUndefined } from '@core/utils'; | 61 | import { isDefined, isUndefined } from '@core/utils'; |
62 | +import { HasUUID } from '../../../../shared/models/id/has-uuid'; | ||
62 | 63 | ||
63 | @Component({ | 64 | @Component({ |
64 | selector: 'tb-entities-table', | 65 | selector: 'tb-entities-table', |
@@ -401,16 +402,19 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn | @@ -401,16 +402,19 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn | ||
401 | true | 402 | true |
402 | ).subscribe((result) => { | 403 | ).subscribe((result) => { |
403 | if (result) { | 404 | if (result) { |
404 | - const tasks: Observable<any>[] = []; | 405 | + const tasks: Observable<HasUUID>[] = []; |
405 | entities.forEach((entity) => { | 406 | entities.forEach((entity) => { |
406 | if (this.entitiesTableConfig.deleteEnabled(entity)) { | 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 | forkJoin(tasks).subscribe( | 414 | forkJoin(tasks).subscribe( |
411 | - () => { | 415 | + (ids) => { |
412 | this.updateData(); | 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,6 +84,8 @@ import { UserFilterDialogComponent } from '@home/components/filter/user-filter-d | ||
84 | import { FilterUserInfoComponent } from './filter/filter-user-info.component'; | 84 | import { FilterUserInfoComponent } from './filter/filter-user-info.component'; |
85 | import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component'; | 85 | import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component'; |
86 | import { FilterPredicateValueComponent } from './filter/filter-predicate-value.component'; | 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 | @NgModule({ | 90 | @NgModule({ |
89 | declarations: | 91 | declarations: |
@@ -150,7 +152,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c | @@ -150,7 +152,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c | ||
150 | UserFilterDialogComponent, | 152 | UserFilterDialogComponent, |
151 | FilterUserInfoComponent, | 153 | FilterUserInfoComponent, |
152 | FilterUserInfoDialogComponent, | 154 | FilterUserInfoDialogComponent, |
153 | - FilterPredicateValueComponent | 155 | + FilterPredicateValueComponent, |
156 | + TenantProfileAutocompleteComponent, | ||
157 | + TenantProfileComponent | ||
154 | ], | 158 | ], |
155 | imports: [ | 159 | imports: [ |
156 | CommonModule, | 160 | CommonModule, |
@@ -206,7 +210,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c | @@ -206,7 +210,9 @@ import { FilterPredicateValueComponent } from './filter/filter-predicate-value.c | ||
206 | FiltersDialogComponent, | 210 | FiltersDialogComponent, |
207 | FilterSelectComponent, | 211 | FilterSelectComponent, |
208 | FiltersEditComponent, | 212 | FiltersEditComponent, |
209 | - UserFilterDialogComponent | 213 | + UserFilterDialogComponent, |
214 | + TenantProfileAutocompleteComponent, | ||
215 | + TenantProfileComponent | ||
210 | ], | 216 | ], |
211 | providers: [ | 217 | providers: [ |
212 | WidgetComponentService, | 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,6 +29,7 @@ import { EntityViewModule } from '@modules/home/pages/entity-view/entity-view.mo | ||
29 | import { RuleChainModule } from '@modules/home/pages/rulechain/rulechain.module'; | 29 | import { RuleChainModule } from '@modules/home/pages/rulechain/rulechain.module'; |
30 | import { WidgetLibraryModule } from '@modules/home/pages/widget/widget-library.module'; | 30 | import { WidgetLibraryModule } from '@modules/home/pages/widget/widget-library.module'; |
31 | import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module'; | 31 | import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module'; |
32 | +import { TenantProfileModule } from './tenant-profile/tenant-profile.module'; | ||
32 | import { MODULES_MAP } from '@shared/public-api'; | 33 | import { MODULES_MAP } from '@shared/public-api'; |
33 | import { modulesMap } from '../../common/modules-map'; | 34 | import { modulesMap } from '../../common/modules-map'; |
34 | 35 | ||
@@ -37,6 +38,7 @@ import { modulesMap } from '../../common/modules-map'; | @@ -37,6 +38,7 @@ import { modulesMap } from '../../common/modules-map'; | ||
37 | AdminModule, | 38 | AdminModule, |
38 | HomeLinksModule, | 39 | HomeLinksModule, |
39 | ProfileModule, | 40 | ProfileModule, |
41 | + TenantProfileModule, | ||
40 | TenantModule, | 42 | TenantModule, |
41 | DeviceModule, | 43 | DeviceModule, |
42 | AssetModule, | 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,6 +49,11 @@ | ||
49 | {{ 'tenant.title-required' | translate }} | 49 | {{ 'tenant.title-required' | translate }} |
50 | </mat-error> | 50 | </mat-error> |
51 | </mat-form-field> | 51 | </mat-form-field> |
52 | + <tb-tenant-profile-autocomplete | ||
53 | + [selectDefaultProfile]="isAdd" | ||
54 | + required | ||
55 | + formControlName="tenantProfileId"> | ||
56 | + </tb-tenant-profile-autocomplete> | ||
52 | <div formGroupName="additionalInfo" fxLayout="column"> | 57 | <div formGroupName="additionalInfo" fxLayout="column"> |
53 | <mat-form-field class="mat-block"> | 58 | <mat-form-field class="mat-block"> |
54 | <mat-label translate>tenant.description</mat-label> | 59 | <mat-label translate>tenant.description</mat-label> |
@@ -56,16 +61,6 @@ | @@ -56,16 +61,6 @@ | ||
56 | </mat-form-field> | 61 | </mat-form-field> |
57 | </div> | 62 | </div> |
58 | <tb-contact [parentForm]="entityForm" [isEdit]="isEdit"></tb-contact> | 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 | </fieldset> | 64 | </fieldset> |
70 | </form> | 65 | </form> |
71 | </div> | 66 | </div> |
@@ -27,7 +27,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod | @@ -27,7 +27,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod | ||
27 | @Component({ | 27 | @Component({ |
28 | selector: 'tb-tenant', | 28 | selector: 'tb-tenant', |
29 | templateUrl: './tenant.component.html', | 29 | templateUrl: './tenant.component.html', |
30 | - styleUrls: ['./tenant.component.scss'] | 30 | + styleUrls: [] |
31 | }) | 31 | }) |
32 | export class TenantComponent extends ContactBasedComponent<TenantInfo> { | 32 | export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
33 | 33 | ||
@@ -52,8 +52,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { | @@ -52,8 +52,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { | ||
52 | { | 52 | { |
53 | title: [entity ? entity.title : '', [Validators.required]], | 53 | title: [entity ? entity.title : '', [Validators.required]], |
54 | tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], | 54 | tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], |
55 | - // isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], | ||
56 | - // isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], | ||
57 | additionalInfo: this.fb.group( | 55 | additionalInfo: this.fb.group( |
58 | { | 56 | { |
59 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] | 57 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] |
@@ -66,8 +64,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { | @@ -66,8 +64,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { | ||
66 | updateEntityForm(entity: Tenant) { | 64 | updateEntityForm(entity: Tenant) { |
67 | this.entityForm.patchValue({title: entity.title}); | 65 | this.entityForm.patchValue({title: entity.title}); |
68 | this.entityForm.patchValue({tenantProfileId: entity.tenantProfileId}); | 66 | this.entityForm.patchValue({tenantProfileId: entity.tenantProfileId}); |
69 | - // this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); | ||
70 | - // this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); | ||
71 | this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); | 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,10 +71,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { | ||
75 | if (this.entityForm) { | 71 | if (this.entityForm) { |
76 | if (this.isEditValue) { | 72 | if (this.isEditValue) { |
77 | this.entityForm.enable({emitEvent: false}); | 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 | } else { | 74 | } else { |
83 | this.entityForm.disable({emitEvent: false}); | 75 | this.entityForm.disable({emitEvent: false}); |
84 | } | 76 | } |
@@ -16,7 +16,16 @@ | @@ -16,7 +16,16 @@ | ||
16 | 16 | ||
17 | import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; | 17 | import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; |
18 | import { HasUUID } from '@shared/models/id/has-uuid'; | 18 | import { HasUUID } from '@shared/models/id/has-uuid'; |
19 | +import { isDefinedAndNotNull } from '@core/utils'; | ||
19 | 20 | ||
20 | export interface EntityId extends HasUUID { | 21 | export interface EntityId extends HasUUID { |
21 | entityType: EntityType | AliasEntityType; | 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,7 +26,7 @@ export interface TenantProfileData { | ||
26 | export interface TenantProfile extends BaseData<TenantProfileId> { | 26 | export interface TenantProfile extends BaseData<TenantProfileId> { |
27 | name: string; | 27 | name: string; |
28 | description: string; | 28 | description: string; |
29 | - isDefault: boolean; | 29 | + default: boolean; |
30 | isolatedTbCore: boolean; | 30 | isolatedTbCore: boolean; |
31 | isolatedTbRuleEngine: boolean; | 31 | isolatedTbRuleEngine: boolean; |
32 | profileData: TenantProfileData; | 32 | profileData: TenantProfileData; |
@@ -1683,7 +1683,23 @@ | @@ -1683,7 +1683,23 @@ | ||
1683 | "tenant-profile-details": "Tenant profile details", | 1683 | "tenant-profile-details": "Tenant profile details", |
1684 | "no-tenant-profiles-text": "No tenant profiles found", | 1684 | "no-tenant-profiles-text": "No tenant profiles found", |
1685 | "search": "Search tenant profiles", | 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 | "timeinterval": { | 1704 | "timeinterval": { |
1689 | "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", | 1705 | "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", |