- +
- + +
+ + +
+
+ + dashboard.state + + + {{ stateItem.value.name }} + + + +
+
+
+ + + +
+ diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/states/select-target-state-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/states/select-target-state-dialog.component.ts new file mode 100644 index 0000000000..0c5b6842ff --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/dashboard/states/select-target-state-dialog.component.ts @@ -0,0 +1,85 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { UtilsService } from '@core/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { DashboardState } from '@app/shared/models/dashboard.models'; +import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; +import { DashboardService } from '@core/http/dashboard.service'; + +export interface SelectTargetStateDialogData { + states: {[id: string]: DashboardState }; +} + +@Component({ + selector: 'tb-select-target-state-dialog', + templateUrl: './select-target-state-dialog.component.html', + providers: [{provide: ErrorStateMatcher, useExisting: SelectTargetStateDialogComponent}], + styleUrls: [] +}) +export class SelectTargetStateDialogComponent extends + DialogComponent + implements OnInit, ErrorStateMatcher { + + states: {[id: string]: DashboardState }; + stateFormGroup: FormGroup; + + submitted = false; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: SelectTargetStateDialogData, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + public dialogRef: MatDialogRef, + private fb: FormBuilder, + private dashboardUtils: DashboardUtilsService) { + super(store, router, dialogRef); + + this.states = this.data.states; + + this.stateFormGroup = this.fb.group( + { + stateId: [this.dashboardUtils.getRootStateId(this.states), [Validators.required]] + } + ); + } + + ngOnInit(): void { + } + + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); + const customErrorState = !!(control && control.invalid && this.submitted); + return originalErrorState || customErrorState; + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + this.submitted = true; + const stateId: string = this.stateFormGroup.get('stateId').value; + this.dialogRef.close(stateId); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts index cfe3e9bf8f..3c21b014e5 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts @@ -60,9 +60,10 @@ export abstract class StateControllerComponent implements IStateControllerCompon set dashboardId(val: string) { if (this.dashboardIdValue !== val) { this.dashboardIdValue = val; - if (this.inited) { +/* if (this.inited) { + this.currentState = this.route.snapshot.queryParamMap.get('state'); this.init(); - } + }*/ } } get dashboardId(): string { @@ -84,8 +85,6 @@ export abstract class StateControllerComponent implements IStateControllerCompon currentState: string; - currentUrl: string; - private rxSubscriptions = new Array(); private inited = false; @@ -96,10 +95,9 @@ export abstract class StateControllerComponent implements IStateControllerCompon } ngOnInit(): void { - this.currentUrl = this.router.url.split('?')[0]; this.rxSubscriptions.push(this.route.queryParamMap.subscribe((paramMap) => { - const newUrl = this.router.url.split('?')[0]; - if (this.currentUrl === newUrl) { + const dashboardId = this.route.snapshot.params.dashboardId; + if (this.dashboardId === dashboardId) { const newState = paramMap.get('state'); if (this.currentState !== newState) { this.currentState = newState; @@ -144,6 +142,11 @@ export abstract class StateControllerComponent implements IStateControllerCompon this.statesControllerService.cleanupPreservedStates(); } + public reInit() { + this.currentState = this.route.snapshot.queryParamMap.get('state'); + this.init(); + } + protected abstract init(); protected abstract onMobileChanged(); diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.models.ts b/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.models.ts index 2fa0fef100..8a1ce9319d 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.models.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.models.ts @@ -27,4 +27,5 @@ export interface IStateControllerComponent extends IStateController { states: {[id: string]: DashboardState }; dashboardId: string; preservedState: any; + reInit(): void; } diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts b/ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts index 48970e36ae..534eb1d6f7 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts @@ -72,6 +72,7 @@ export class StatesComponentDirective implements OnInit, OnDestroy, OnChanges { } ngOnChanges(changes: SimpleChanges): void { + let reInitController = false; for (const propName of Object.keys(changes)) { const change = changes[propName]; if (!change.firstChange && change.currentValue !== change.previousValue) { @@ -81,6 +82,7 @@ export class StatesComponentDirective implements OnInit, OnDestroy, OnChanges { this.stateControllerComponent.states = this.states; } else if (propName === 'dashboardId') { this.stateControllerComponent.dashboardId = this.dashboardId; + reInitController = true; } else if (propName === 'isMobile') { this.stateControllerComponent.isMobile = this.isMobile; } else if (propName === 'state') { @@ -88,6 +90,9 @@ export class StatesComponentDirective implements OnInit, OnDestroy, OnChanges { } } } + if (reInitController) { + this.stateControllerComponent.reInit(); + } } private reInit() { diff --git a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html index a4d260f91b..f0862dbe52 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html @@ -19,6 +19,7 @@ label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> @@ -26,6 +27,7 @@ label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> diff --git a/ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts index b916313ce9..555bbd0499 100644 --- a/ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts @@ -14,51 +14,53 @@ /// limitations under the License. /// -import {Injectable} from '@angular/core'; +import { Injectable } from '@angular/core'; -import {ActivatedRouteSnapshot, Resolve, Router} from '@angular/router'; +import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; import { CellActionDescriptor, checkBoxCell, DateEntityTableColumn, EntityTableColumn, - EntityTableConfig, GroupActionDescriptor, + EntityTableConfig, + GroupActionDescriptor, HeaderActionDescriptor } from '@home/models/entity/entities-table-config.models'; -import {TranslateService} from '@ngx-translate/core'; -import {DatePipe} from '@angular/common'; -import {EntityType, entityTypeResources, entityTypeTranslations} from '@shared/models/entity-type.models'; -import {EntityAction} from '@home/models/entity/entity-component.models'; -import {Device, DeviceCredentials, DeviceInfo} from '@app/shared/models/device.models'; -import {DeviceComponent} from '@modules/home/pages/device/device.component'; -import {forkJoin, Observable, of} from 'rxjs'; -import {select, Store} from '@ngrx/store'; -import {selectAuthUser} from '@core/auth/auth.selectors'; -import {map, mergeMap, take, tap} from 'rxjs/operators'; -import {AppState} from '@core/core.state'; -import {DeviceService} from '@app/core/http/device.service'; -import {Authority} from '@app/shared/models/authority.enum'; -import {CustomerService} from '@core/http/customer.service'; -import {Customer} from '@app/shared/models/customer.model'; -import {NULL_UUID} from '@shared/models/id/has-uuid'; -import {BroadcastService} from '@core/services/broadcast.service'; -import {DeviceTableHeaderComponent} from '@modules/home/pages/device/device-table-header.component'; -import {MatDialog} from '@angular/material'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { Device, DeviceCredentials, DeviceInfo } from '@app/shared/models/device.models'; +import { DeviceComponent } from '@modules/home/pages/device/device.component'; +import { forkJoin, Observable, of } from 'rxjs'; +import { select, Store } from '@ngrx/store'; +import { selectAuthUser } from '@core/auth/auth.selectors'; +import { map, mergeMap, take, tap } from 'rxjs/operators'; +import { AppState } from '@core/core.state'; +import { DeviceService } from '@app/core/http/device.service'; +import { Authority } from '@app/shared/models/authority.enum'; +import { CustomerService } from '@core/http/customer.service'; +import { Customer } from '@app/shared/models/customer.model'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { BroadcastService } from '@core/services/broadcast.service'; +import { DeviceTableHeaderComponent } from '@modules/home/pages/device/device-table-header.component'; +import { MatDialog } from '@angular/material'; import { DeviceCredentialsDialogComponent, DeviceCredentialsDialogData } from '@modules/home/pages/device/device-credentials-dialog.component'; -import {DialogService} from '@core/services/dialog.service'; +import { DialogService } from '@core/services/dialog.service'; import { AssignToCustomerDialogComponent, AssignToCustomerDialogData } from '@modules/home/dialogs/assign-to-customer-dialog.component'; -import {DeviceId} from '@app/shared/models/id/device-id'; +import { DeviceId } from '@app/shared/models/id/device-id'; import { AddEntitiesToCustomerDialogComponent, AddEntitiesToCustomerDialogData } from '../../dialogs/add-entities-to-customer-dialog.component'; import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component'; +import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; @Injectable() export class DevicesTableConfigResolver implements Resolve> { @@ -72,6 +74,7 @@ export class DevicesTableConfigResolver implements Resolve { + if (res) { + this.broadcast.broadcast('deviceSaved'); + this.config.table.updateData(); + } + }); } addDevicesToCustomer($event: Event) { diff --git a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html index 3de07d16e7..33d183389e 100644 --- a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html @@ -19,6 +19,7 @@ label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> @@ -26,6 +27,7 @@ label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html index 1c5b2b55e0..0118d1f5f2 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html @@ -19,6 +19,7 @@ label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> @@ -26,6 +27,7 @@ label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html index b48d44b826..38d717742a 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html @@ -19,6 +19,7 @@ label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> @@ -26,6 +27,7 @@ label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.ts b/ui-ngx/src/app/modules/login/pages/login/login.component.ts index ca75c1f4b8..b893f729f3 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.ts @@ -29,7 +29,10 @@ import { FormBuilder } from '@angular/forms'; }) export class LoginComponent extends PageComponent implements OnInit { - loginFormGroup = this.fb.group(new LoginRequest('', '')); + loginFormGroup = this.fb.group({ + username: '', + password: '' + }); constructor(protected store: Store, private authService: AuthService, diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts index f4743ab4c5..65b4337c75 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts @@ -192,19 +192,22 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI const authUser = getCurrentAuthUser(this.store); if (this.dashboardsScope === 'customer' || authUser.authority === Authority.CUSTOMER_USER) { if (this.customerId) { - dashboardsObservable = this.dashboardService.getCustomerDashboards(this.customerId, pageLink, false, true); + dashboardsObservable = this.dashboardService.getCustomerDashboards(this.customerId, pageLink, + {ignoreLoading: true}); } else { dashboardsObservable = of(emptyPageData()); } } else { if (authUser.authority === Authority.SYS_ADMIN) { if (this.tenantId) { - dashboardsObservable = this.dashboardService.getTenantDashboardsByTenantId(this.tenantId, pageLink, false, true); + dashboardsObservable = this.dashboardService.getTenantDashboardsByTenantId(this.tenantId, pageLink, + {ignoreLoading: true}); } else { dashboardsObservable = of(emptyPageData()); } } else { - dashboardsObservable = this.dashboardService.getTenantDashboards(pageLink, false, true); + dashboardsObservable = this.dashboardService.getTenantDashboards(pageLink, + {ignoreLoading: true}); } } return dashboardsObservable; diff --git a/ui-ngx/src/app/shared/components/dashboard-select.component.ts b/ui-ngx/src/app/shared/components/dashboard-select.component.ts index 341dd9e34e..c78ef98cc2 100644 --- a/ui-ngx/src/app/shared/components/dashboard-select.component.ts +++ b/ui-ngx/src/app/shared/components/dashboard-select.component.ts @@ -202,12 +202,13 @@ export class DashboardSelectComponent implements ControlValueAccessor, OnInit { const authUser = getCurrentAuthUser(this.store); if (this.dashboardsScope === 'customer' || authUser.authority === Authority.CUSTOMER_USER) { if (this.customerId && this.customerId !== NULL_UUID) { - dashboardsObservable = this.dashboardService.getCustomerDashboards(this.customerId, pageLink, false, true); + dashboardsObservable = this.dashboardService.getCustomerDashboards(this.customerId, pageLink, + {ignoreLoading: true}); } else { dashboardsObservable = of(emptyPageData()); } } else { - dashboardsObservable = this.dashboardService.getTenantDashboards(pageLink, false, true); + dashboardsObservable = this.dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); } return dashboardsObservable; } diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts index 41985ac384..815c3ba8da 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts @@ -230,7 +230,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit if (targetEntityType === AliasEntityType.CURRENT_CUSTOMER) { targetEntityType = EntityType.CUSTOMER; } - this.entityService.getEntity(targetEntityType, value, true).subscribe( + this.entityService.getEntity(targetEntityType, value, {ignoreLoading: true}).subscribe( (entity) => { this.modelValue = entity.id.id; this.selectEntityFormGroup.get('entity').patchValue(entity, {emitEvent: false}); @@ -238,7 +238,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit ); } else { const targetEntityType = value.entityType as EntityType; - this.entityService.getEntity(targetEntityType, value.id, true).subscribe( + this.entityService.getEntity(targetEntityType, value.id, {ignoreLoading: true}).subscribe( (entity) => { this.modelValue = entity.id.id; this.selectEntityFormGroup.get('entity').patchValue(entity, {emitEvent: false}); @@ -281,7 +281,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit targetEntityType = EntityType.CUSTOMER; } return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, - 50, this.entitySubtypeValue, false, true).pipe( + 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe( map((data) => { if (data) { if (this.excludeEntityIds && this.excludeEntityIds.length) { diff --git a/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts index a9aca0725a..dc006ecf57 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts @@ -198,7 +198,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af fetchKeys(searchText?: string): Observable> { this.searchText = searchText; return this.entityIdValue ? this.entityService.getEntityKeys(this.entityIdValue, searchText, - this.dataKeyType, false, true).pipe( + this.dataKeyType, {ignoreLoading: true}).pipe( map((data) => data ? data : [])) : of([]); } diff --git a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts index db373c80f0..54cb2f37db 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts @@ -223,7 +223,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV fetchEntities(searchText?: string): Observable>> { this.searchText = searchText; return this.entityService.getEntitiesByNameFilter(this.entityType, searchText, - 50, '', false, true).pipe( + 50, '', {ignoreLoading: true}).pipe( map((data) => data ? data : [])); } diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts index 9983254072..7e809471bf 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts @@ -205,13 +205,13 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, let subTypesObservable: Observable>; switch (this.entityType) { case EntityType.ASSET: - subTypesObservable = this.assetService.getAssetTypes(false, true); + subTypesObservable = this.assetService.getAssetTypes({ignoreLoading: true}); break; case EntityType.DEVICE: - subTypesObservable = this.deviceService.getDeviceTypes(false, true); + subTypesObservable = this.deviceService.getDeviceTypes({ignoreLoading: true}); break; case EntityType.ENTITY_VIEW: - subTypesObservable = this.entityViewService.getEntityViewTypes(false, true); + subTypesObservable = this.entityViewService.getEntityViewTypes({ignoreLoading: true}); break; } if (subTypesObservable) { diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index a98e06210c..58ed2600b5 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -276,13 +276,13 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, let subTypesObservable: Observable>; switch (this.entityType) { case EntityType.ASSET: - subTypesObservable = this.assetService.getAssetTypes(false, true); + subTypesObservable = this.assetService.getAssetTypes({ignoreLoading: true}); break; case EntityType.DEVICE: - subTypesObservable = this.deviceService.getDeviceTypes(false, true); + subTypesObservable = this.deviceService.getDeviceTypes({ignoreLoading: true}); break; case EntityType.ENTITY_VIEW: - subTypesObservable = this.entityViewService.getEntityViewTypes(false, true); + subTypesObservable = this.entityViewService.getEntityViewTypes({ignoreLoading: true}); break; } if (subTypesObservable) { diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.html index 96f0b679c7..9877f9b66f 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.html @@ -17,8 +17,7 @@ --> {{ entitySubtypeTitle | translate }} - + {{ displaySubTypeFn(subType) }} diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.ts index 106f869d80..d446a3e2ca 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-select.component.ts @@ -211,13 +211,13 @@ export class EntitySubTypeSelectComponent implements ControlValueAccessor, OnIni if (!this.subTypes) { switch (this.entityType) { case EntityType.ASSET: - this.subTypes = this.assetService.getAssetTypes(false, true); + this.subTypes = this.assetService.getAssetTypes({ignoreLoading: true}); break; case EntityType.DEVICE: - this.subTypes = this.deviceService.getDeviceTypes(false, true); + this.subTypes = this.deviceService.getDeviceTypes({ignoreLoading: true}); break; case EntityType.ENTITY_VIEW: - this.subTypes = this.entityViewService.getEntityViewTypes(false, true); + this.subTypes = this.entityViewService.getEntityViewTypes({ignoreLoading: true}); break; } if (this.subTypes) { @@ -227,6 +227,20 @@ export class EntitySubTypeSelectComponent implements ControlValueAccessor, OnIni this.subTypesLoaded = true; return allSubtypes; }), + tap((subTypes) => { + const type: EntitySubtype | string = this.subTypeFormGroup.get('subType').value; + const strType = typeof type === 'string' ? type : type.type; + const found = subTypes.find((subType) => { + if (typeof subType === 'string') { + return subType === type; + } else { + return subType.type === strType; + } + }); + if (found) { + this.subTypeFormGroup.get('subType').patchValue(found); + } + }), publishReplay(1), refCount() ); diff --git a/ui-ngx/src/app/shared/components/fullscreen.directive.ts b/ui-ngx/src/app/shared/components/fullscreen.directive.ts index d8b3827036..d21ded3b61 100644 --- a/ui-ngx/src/app/shared/components/fullscreen.directive.ts +++ b/ui-ngx/src/app/shared/components/fullscreen.directive.ts @@ -107,7 +107,9 @@ export class FullscreenDirective implements OnChanges, OnDestroy { if (this.elementRef) { this.elementRef.nativeElement.classList.remove('tb-fullscreen'); } - this.overlayRef.dispose(); + if (this.overlayRef) { + this.overlayRef.dispose(); + } this.fullscreenChanged.emit(false); } } diff --git a/ui-ngx/src/app/shared/models/asset.models.ts b/ui-ngx/src/app/shared/models/asset.models.ts index c7b5c41563..ba29f904f8 100644 --- a/ui-ngx/src/app/shared/models/asset.models.ts +++ b/ui-ngx/src/app/shared/models/asset.models.ts @@ -22,8 +22,8 @@ import {DeviceCredentialsId} from '@shared/models/id/device-credentials-id'; import { EntitySearchQuery } from '@shared/models/relation.models'; export interface Asset extends BaseData { - tenantId: TenantId; - customerId: CustomerId; + tenantId?: TenantId; + customerId?: CustomerId; name: string; type: string; additionalInfo?: any; diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 31edec6cfe..96a4fe782c 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -63,6 +63,7 @@ export const HelpLinks = { devices: helpBaseUrl + '/docs/user-guide/ui/devices', assets: helpBaseUrl + '/docs/user-guide/ui/assets', entityViews: helpBaseUrl + '/docs/user-guide/ui/entity-views', + entitiesImport: helpBaseUrl + '/docs/user-guide/bulk-provisioning', rulechains: helpBaseUrl + '/docs/user-guide/ui/rule-chains', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index 6c12e61a44..44d4fb1727 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -98,7 +98,6 @@ export interface DashboardConfiguration { states?: {[id: string]: DashboardState }; entityAliases?: EntityAliases; [key: string]: any; - // TODO: } export interface Dashboard extends DashboardInfo { diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 6ddd015a24..3ab95fc73e 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -22,11 +22,11 @@ import {DeviceCredentialsId} from '@shared/models/id/device-credentials-id'; import { EntitySearchQuery } from '@shared/models/relation.models'; export interface Device extends BaseData { - tenantId: TenantId; - customerId: CustomerId; + tenantId?: TenantId; + customerId?: CustomerId; name: string; type: string; - label: string; + label?: string; additionalInfo?: any; } diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index b1349986fa..aab052f213 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -17,6 +17,7 @@ import { BaseData } from '@shared/models/base-data'; import { EntityType } from '@shared/models/entity-type.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { AttributeData } from './telemetry/telemetry.models'; export interface EntityInfo { origEntity?: BaseData; @@ -26,3 +27,26 @@ export interface EntityInfo { id?: string; entityDescription?: string; } + +export interface ImportEntityData { + name: string; + type: string; + accessToken: string; + attributes: { + server: AttributeData[], + shared: AttributeData[] + }; + timeseries: AttributeData[]; +} + +export interface ImportEntitiesResultInfo { + create?: { + entity: number; + }; + update?: { + entity: number; + }; + error?: { + entity: number; + }; +} diff --git a/ui-ngx/src/app/shared/models/login.models.ts b/ui-ngx/src/app/shared/models/login.models.ts index 9d11359f5a..25905f923d 100644 --- a/ui-ngx/src/app/shared/models/login.models.ts +++ b/ui-ngx/src/app/shared/models/login.models.ts @@ -14,17 +14,16 @@ /// limitations under the License. /// -export class LoginRequest { +export interface LoginRequest { username: string; password: string; +} - constructor(username: string, password: string) { - this.username = username; - this.password = password; - } +export interface PublicLoginRequest { + publicId: string; } -export class LoginResponse { +export interface LoginResponse { token: string; refreshToken: string; } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 147967b3e7..2a1832df27 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -50,3 +50,8 @@ export interface UserPasswordPolicy { export interface SecuritySettings { passwordPolicy: UserPasswordPolicy; } + +export interface UpdateMessage { + message: string; + updateAvailable: boolean; +} diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index baacc18006..d0c837332a 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -20,6 +20,7 @@ import { AggregationType } from '../time/time.models'; import { Observable, ReplaySubject, Subject } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; import { map } from 'rxjs/operators'; +import { NgZone } from '@angular/core'; export enum DataKeyType { timeseries = 'timeseries', @@ -64,7 +65,7 @@ export const isClientSideTelemetryType = new Map( ); export interface AttributeData { - lastUpdateTs: number; + lastUpdateTs?: number; key: string; value: any; } @@ -231,6 +232,8 @@ export class TelemetrySubscriber { private dataSubject = new ReplaySubject(); private reconnectSubject = new Subject(); + private zone: NgZone; + public subscriptionCommands: Array; public data$ = this.dataSubject.asObservable(); @@ -238,7 +241,7 @@ export class TelemetrySubscriber { public static createEntityAttributesSubscription(telemetryService: TelemetryService, entityId: EntityId, attributeScope: TelemetryType, - keys: string[] = null): TelemetrySubscriber { + zone: NgZone, keys: string[] = null): TelemetrySubscriber { let subscriptionCommand: SubscriptionCmd; if (attributeScope === LatestTelemetry.LATEST_TELEMETRY) { subscriptionCommand = new TimeseriesSubscriptionCmd(); @@ -252,6 +255,7 @@ export class TelemetrySubscriber { subscriptionCommand.keys = keys.join(','); } const subscriber = new TelemetrySubscriber(telemetryService); + subscriber.zone = zone; subscriber.subscriptionCommands.push(subscriptionCommand); return subscriber; } @@ -280,7 +284,15 @@ export class TelemetrySubscriber { } } message.prepareData(keys); - this.dataSubject.next(message); + if (this.zone) { + this.zone.run( + () => { + this.dataSubject.next(message); + } + ); + } else { + this.dataSubject.next(message); + } } public onReconnected() { diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 5e040d8b51..ab42427e58 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -262,7 +262,6 @@ export interface Datasource { entityDescription?: string; generated?: boolean; [key: string]: any; - // TODO: } export type DataSet = [number, any][]; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index d5d4bd73e4..31200899d8 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -61,6 +61,7 @@ import { RouterModule } from '@angular/router'; import { ShareModule as ShareButtonsModule } from '@ngx-share/core'; import { HotkeyModule } from 'angular2-hotkeys'; import { ColorPickerModule } from 'ngx-color-picker'; +import { NgxHmCarouselModule } from 'ngx-hm-carousel'; import { UserMenuComponent } from '@shared/components/user-menu.component'; import { NospacePipe } from './pipe/nospace.pipe'; import { TranslateModule } from '@ngx-translate/core'; @@ -233,6 +234,7 @@ import { FileInputComponent } from './components/file-input.component'; ShareButtonsModule, HotkeyModule, ColorPickerModule, + NgxHmCarouselModule, NgxFlowModule ], exports: [ @@ -314,6 +316,7 @@ import { FileInputComponent } from './components/file-input.component'; ShareButtonsModule, HotkeyModule, ColorPickerModule, + NgxHmCarouselModule, ColorPickerDialogComponent, MaterialIconsDialogComponent, ColorInputComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9f85270dfd..d806f61f42 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -52,7 +52,8 @@ "import": "Import", "export": "Export", "share-via": "Share via {{provider}}", - "continue": "Continue" + "continue": "Continue", + "back": "Back" }, "aggregation": { "aggregation": "Aggregation",