Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/4852/head
Andrii Shvaika 5 years ago
parent
commit
bbe7dc0e6f
  1. 40
      application/src/main/data/json/demo/dashboards/firmware.json
  2. 40
      application/src/main/data/json/demo/dashboards/software.json
  3. 2
      application/src/main/resources/thingsboard.yml
  4. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  5. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  6. 4
      pom.xml
  7. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  8. 10
      ui-ngx/src/app/core/http/entity.service.ts
  9. 14
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts
  10. 3
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  11. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts
  12. 8
      ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts
  13. 3
      ui-ngx/src/app/modules/home/components/dashboard-page/states/state-controller.component.ts
  14. 63
      ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts
  15. 4
      ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts
  16. 23
      ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts
  17. 16
      ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts
  18. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html
  19. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html
  20. 6
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss
  21. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html
  23. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.scss
  24. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts
  25. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  26. 5
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss
  27. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts
  28. 10
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts
  29. 15
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts
  30. 2
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts
  31. 5
      ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
  32. 2
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  33. 6
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  34. 2
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  35. 2
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  36. 2
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  37. 7
      ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts
  38. 9
      ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts
  39. 18
      ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts
  40. 11
      ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts
  41. 18
      ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts
  42. 2
      ui-ngx/src/app/shared/components/time/timewindow-panel.component.html
  43. 2
      ui-ngx/src/app/shared/components/time/timewindow.component.ts
  44. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

40
application/src/main/data/json/demo/dashboards/firmware.json

@ -33,15 +33,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": false, "enableStickyAction": false,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -828,15 +828,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1125,15 +1125,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1422,15 +1422,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1719,15 +1719,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,

40
application/src/main/data/json/demo/dashboards/software.json

@ -33,15 +33,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": false, "enableStickyAction": false,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -828,15 +828,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1125,15 +1125,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1422,15 +1422,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,
@ -1719,15 +1719,15 @@
"enableSearch": true, "enableSearch": true,
"displayPagination": true, "displayPagination": true,
"defaultPageSize": 10, "defaultPageSize": 10,
"defaultSortOrder": "entityLabel", "defaultSortOrder": "entityName",
"displayEntityName": false, "displayEntityName": true,
"displayEntityType": false, "displayEntityType": false,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": true,
"entitiesTitle": "Devices", "entitiesTitle": "Devices",
"displayEntityLabel": true, "displayEntityLabel": false,
"entityLabelColumnTitle": "Device" "entityNameColumnTitle": "Device"
}, },
"title": "New Entities table", "title": "New Entities table",
"dropShadow": true, "dropShadow": true,

2
application/src/main/resources/thingsboard.yml

@ -518,7 +518,7 @@ audit-log:
index_pattern: "${AUDIT_LOG_SINK_INDEX_PATTERN:@{TENANT}_AUDIT_LOG_@{DATE}}" index_pattern: "${AUDIT_LOG_SINK_INDEX_PATTERN:@{TENANT}_AUDIT_LOG_@{DATE}}"
# Date format. Details of the pattern could be found here: # Date format. Details of the pattern could be found here:
# https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html # https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.DD}" date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.dd}"
scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https
host: "${AUDIT_LOG_SINK_HOST:localhost}" host: "${AUDIT_LOG_SINK_HOST:localhost}"
port: "${AUDIT_LOG_SINK_PORT:9200}" port: "${AUDIT_LOG_SINK_PORT:9200}"

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager; import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer; import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
@ -133,13 +134,14 @@ public class LwM2MTransportBootstrapService {
private boolean setBuilderX509(LeshanBootstrapServerBuilder builder) { private boolean setBuilderX509(LeshanBootstrapServerBuilder builder) {
try { try {
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(this.bootstrapConfig.getCertificateAlias()); X509Certificate[] certificateChain = SslContextUtil.asX509Certificates(serverConfig.getKeyStoreValue().getCertificateChain(this.bootstrapConfig.getCertificateAlias()));
X509Certificate serverCertificate = certificateChain[0];
PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.bootstrapConfig.getCertificateAlias(), serverConfig.getCertificatePassword() == null ? null : serverConfig.getCertificatePassword().toCharArray()); PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.bootstrapConfig.getCertificateAlias(), serverConfig.getCertificatePassword() == null ? null : serverConfig.getCertificatePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey(); PublicKey publicKey = serverCertificate.getPublicKey();
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey); builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); builder.setCertificateChain(certificateChain);
return true; return true;
} else { } else {
return false; return false;

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
@ -156,13 +157,14 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private boolean setBuilderX509(LeshanServerBuilder builder) { private boolean setBuilderX509(LeshanServerBuilder builder) {
try { try {
X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias()); X509Certificate[] certificateChain = SslContextUtil.asX509Certificates(config.getKeyStoreValue().getCertificateChain(config.getCertificateAlias()));
X509Certificate serverCertificate = certificateChain[0];
PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getCertificatePassword() == null ? null : config.getCertificatePassword().toCharArray()); PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getCertificatePassword() == null ? null : config.getCertificatePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey(); PublicKey publicKey = serverCertificate.getPublicKey();
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey); builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); builder.setCertificateChain(certificateChain);
return true; return true;
} else { } else {
return false; return false;

4
pom.xml

@ -80,7 +80,7 @@
<lombok.version>1.18.18</lombok.version> <lombok.version>1.18.18</lombok.version>
<paho.client.version>1.2.4</paho.client.version> <paho.client.version>1.2.4</paho.client.version>
<netty.version>4.1.60.Final</netty.version> <netty.version>4.1.60.Final</netty.version>
<os-maven-plugin.version>1.5.0</os-maven-plugin.version> <os-maven-plugin.version>1.7.0</os-maven-plugin.version>
<rabbitmq.version>4.8.0</rabbitmq.version> <rabbitmq.version>4.8.0</rabbitmq.version>
<surfire.version>2.19.1</surfire.version> <surfire.version>2.19.1</surfire.version>
<jar-plugin.version>3.0.2</jar-plugin.version> <jar-plugin.version>3.0.2</jar-plugin.version>
@ -580,7 +580,7 @@
<extension> <extension>
<groupId>kr.motd.maven</groupId> <groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId> <artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version> <version>${os-maven-plugin.version}</version>
</extension> </extension>
</extensions> </extensions>
<pluginManagement> <pluginManagement>

2
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

10
ui-ngx/src/app/core/http/entity.service.ts

@ -19,7 +19,7 @@ import { EMPTY, forkJoin, Observable, of, throwError } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data'; import { BaseData, HasId } from '@shared/models/base-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
import { TenantService } from '@core/http/tenant.service'; import { TenantService } from '@core/http/tenant.service';
@ -77,8 +77,8 @@ import {
import { alarmFields } from '@shared/models/alarm.models'; import { alarmFields } from '@shared/models/alarm.models';
import { OtaPackageService } from '@core/http/ota-package.service'; import { OtaPackageService } from '@core/http/ota-package.service';
import { EdgeService } from '@core/http/edge.service'; import { EdgeService } from '@core/http/edge.service';
import { Edge, EdgeEventType } from '@shared/models/edge.models'; import { Edge, EdgeEvent, EdgeEventType } from '@shared/models/edge.models';
import { RuleChainType } from '@shared/models/rule-chain.models'; import { RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { DeviceProfileService } from '@core/http/device-profile.service'; import { DeviceProfileService } from '@core/http/device-profile.service';
@ -1342,8 +1342,8 @@ export class EntityService {
return entitiesObservable; return entitiesObservable;
} }
public getEdgeEventContentByEntityType(entity: any): Observable<any> { public getEdgeEventContent(entity: EdgeEvent): Observable<BaseData<HasId> | RuleChainMetaData | string> {
let entityObservable: Observable<any>; let entityObservable: Observable<BaseData<HasId> | RuleChainMetaData | string>;
const entityId: string = entity.entityId; const entityId: string = entity.entityId;
const entityType: any = entity.type; const entityType: any = entity.type;
switch (entityType) { switch (entityType) {

14
ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts

@ -16,9 +16,9 @@
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, startWith, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators';
import { PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -88,6 +88,7 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
ngOnInit() { ngOnInit() {
this.filteredEntityInfos = this.selectEntityInfoFormGroup.get('entityInfo').valueChanges this.filteredEntityInfos = this.selectEntityInfoFormGroup.get('entityInfo').valueChanges
.pipe( .pipe(
debounceTime(150),
tap(value => { tap(value => {
let modelValue; let modelValue;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -99,7 +100,8 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
}), }),
startWith<string | EntityInfo>(''), startWith<string | EntityInfo>(''),
map(value => value ? (typeof value === 'string' ? value : value.name) : ''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchEntityInfos(name) ) distinctUntilChanged(),
switchMap(name => this.fetchEntityInfos(name))
); );
} }
@ -142,7 +144,9 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
} }
getEntityInfos(searchText: string): Observable<PageData<EntityInfo>> { getEntityInfos(searchText: string): Observable<PageData<EntityInfo>> {
return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true}); return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true}).pipe(
catchError(() => of(emptyPageData<EntityInfo>()))
);
} }
clear() { clear() {

3
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -1298,6 +1298,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
types: widgetTypesList, types: widgetTypesList,
typesUpdated: (newTypes) => { typesUpdated: (newTypes) => {
this.filterWidgetTypes = newTypes.filter(type => type.display).map(type => type.type); this.filterWidgetTypes = newTypes.filter(type => type.display).map(type => type.type);
this.cd.markForCheck();
} }
} as DisplayWidgetTypesPanelData } as DisplayWidgetTypesPanelData
}, },
@ -1308,7 +1309,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
]; ];
const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
overlayRef.attach(new ComponentPortal(DisplayWidgetTypesPanelComponent, this.viewContainerRef, injector)); overlayRef.attach(new ComponentPortal(DisplayWidgetTypesPanelComponent, this.viewContainerRef, injector));
this.cd.detectChanges(); this.cd.markForCheck();
} }
onCloseSearchBundle() { onCloseSearchBundle() {

6
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
@ -88,7 +88,8 @@ export class DashboardWidgetSelectComponent implements OnInit {
widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>();
constructor(private widgetsService: WidgetService, constructor(private widgetsService: WidgetService,
private sanitizer: DomSanitizer) { private sanitizer: DomSanitizer,
private cd: ChangeDetectorRef) {
this.widgetsBundles$ = this.search$.asObservable().pipe( this.widgetsBundles$ = this.search$.asObservable().pipe(
distinctUntilChanged(), distinctUntilChanged(),
switchMap(search => this.fetchWidgetBundle(search)) switchMap(search => this.fetchWidgetBundle(search))
@ -128,6 +129,7 @@ export class DashboardWidgetSelectComponent implements OnInit {
); );
setTimeout(() => { setTimeout(() => {
this.widgetTypes = widgetTypes; this.widgetTypes = widgetTypes;
this.cd.markForCheck();
}); });
return widgetInfos; return widgetInfos;
}), }),

8
ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts

@ -275,15 +275,15 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
} }
private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) { private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) {
update = update && this.dashboardCtrl.dashboardCtx.state !== stateId; const isStateIdChanged = this.dashboardCtrl.dashboardCtx.state !== stateId;
this.dashboardCtrl.openDashboardState(stateId, openRightLayout); this.dashboardCtrl.openDashboardState(stateId, openRightLayout);
this.mobileService.handleDashboardStateName(this.getStateName(this.stateObject.length - 1)); this.mobileService.handleDashboardStateName(this.getStateName(this.stateObject.length - 1));
if (update) { if (update) {
this.updateLocation(); this.updateLocation(isStateIdChanged);
} }
} }
private updateLocation() { private updateLocation(isStateIdChanged: boolean) {
if (this.stateObject[this.stateObject.length - 1].id) { if (this.stateObject[this.stateObject.length - 1].id) {
let newState; let newState;
if (this.isDefaultState()) { if (this.isDefaultState()) {
@ -291,7 +291,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
} else { } else {
newState = objToBase64URI(this.stateObject); newState = objToBase64URI(this.stateObject);
} }
this.updateStateParam(newState); this.updateStateParam(newState, !isStateIdChanged);
} }
} }

3
ui-ngx/src/app/modules/home/components/dashboard-page/states/state-controller.component.ts

@ -126,7 +126,7 @@ export abstract class StateControllerComponent implements IStateControllerCompon
this.rxSubscriptions.length = 0; this.rxSubscriptions.length = 0;
} }
protected updateStateParam(newState: string) { protected updateStateParam(newState: string, replaceCurrentHistoryUrl = false) {
this.currentState = newState; this.currentState = newState;
if (this.syncStateWithQueryParam) { if (this.syncStateWithQueryParam) {
const queryParams: Params = {state: this.currentState}; const queryParams: Params = {state: this.currentState};
@ -137,6 +137,7 @@ export abstract class StateControllerComponent implements IStateControllerCompon
relativeTo: this.route, relativeTo: this.route,
queryParams, queryParams,
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
replaceUrl: replaceCurrentHistoryUrl
}); });
}); });
} }

63
ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts

@ -22,6 +22,7 @@ import {
} from '@home/models/entity/entities-table-config.models'; } from '@home/models/entity/entities-table-config.models';
import { import {
EdgeEvent, EdgeEvent,
EdgeEventActionType,
edgeEventActionTypeTranslations, edgeEventActionTypeTranslations,
EdgeEventStatus, EdgeEventStatus,
edgeEventStatusColor, edgeEventStatusColor,
@ -33,7 +34,6 @@ import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
@ -49,18 +49,22 @@ import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-dow
import { EdgeService } from '@core/http/edge.service'; import { EdgeService } from '@core/http/edge.service';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { EntityService } from "@core/http/entity.service"; import { EntityService } from "@core/http/entity.service";
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { ActionNotificationShow } from '@core/notification/notification.actions';
export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePageLink> { export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePageLink> {
queueStartTs: number; queueStartTs: number;
constructor(private edgeService: EdgeService, constructor(private attributeService: AttributeService,
private entityService: EntityService,
private dialogService: DialogService,
private translate: TranslateService,
private attributeService: AttributeService,
private datePipe: DatePipe, private datePipe: DatePipe,
private dialogService: DialogService,
private dialog: MatDialog, private dialog: MatDialog,
private edgeService: EdgeService,
private entityService: EntityService,
private translate: TranslateService,
private store: Store<AppState>,
public entityId: EntityId) { public entityId: EntityId) {
super(); super();
@ -74,12 +78,9 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
this.entitiesDeleteEnabled = false; this.entitiesDeleteEnabled = false;
this.headerComponent = EdgeDownlinkTableHeaderComponent; this.headerComponent = EdgeDownlinkTableHeaderComponent;
this.entityTranslations = { this.entityTranslations = { noEntities: 'edge.no-downlinks-prompt' };
noEntities: 'edge.no-downlinks-prompt'
};
this.entityResources = {} as EntityTypeResource<EdgeEvent>;
this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink); this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink);
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.defaultSortOrder = { property: 'createdTime', direction: Direction.DESC };
this.updateColumns(); this.updateColumns();
} }
@ -96,7 +97,7 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
); );
} }
onUpdate(attributes) { onUpdate(attributes): void {
this.queueStartTs = 0; this.queueStartTs = 0;
let edge = attributes.reduce(function (map, attribute) { let edge = attributes.reduce(function (map, attribute) {
map[attribute.key] = attribute; map[attribute.key] = attribute;
@ -126,12 +127,13 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
{ {
name: this.translate.instant('action.view'), name: this.translate.instant('action.view'),
icon: 'more_horiz', icon: 'more_horiz',
isEnabled: (entity) => this.isEdgeEventHasData(entity.type), isEnabled: (entity) => this.isEdgeEventHasData(entity),
onAction: ($event, entity) => onAction: ($event, entity) =>
{ {
this.prepareEdgeEventContent(entity).subscribe((content) => { this.prepareEdgeEventContent(entity).subscribe(
this.showEdgeEventContent($event, content,'event.data'); (content) => this.showEdgeEventContent($event, content,'event.data'),
}); () => this.showEntityNotFoundError()
);
} }
}, },
'40px'), '40px'),
@ -141,7 +143,7 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
} }
} }
updateEdgeEventStatus(createdTime): string { updateEdgeEventStatus(createdTime: number): string {
if (this.queueStartTs && createdTime < this.queueStartTs) { if (this.queueStartTs && createdTime < this.queueStartTs) {
return this.translate.instant('edge.deployed'); return this.translate.instant('edge.deployed');
} else { } else {
@ -149,21 +151,17 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
} }
} }
isPending(createdTime): boolean { isPending(createdTime: number): boolean {
return createdTime > this.queueStartTs; return createdTime > this.queueStartTs;
} }
isEdgeEventHasData(edgeEventType: EdgeEventType): boolean { isEdgeEventHasData(entity: EdgeEvent): boolean {
switch (edgeEventType) { return !(entity.type === EdgeEventType.ADMIN_SETTINGS ||
case EdgeEventType.ADMIN_SETTINGS: entity.action === EdgeEventActionType.DELETED);
return false;
default:
return true;
}
} }
prepareEdgeEventContent(entity: any): Observable<string> { prepareEdgeEventContent(entity: EdgeEvent): Observable<string> {
return this.entityService.getEdgeEventContentByEntityType(entity).pipe( return this.entityService.getEdgeEventContent(entity).pipe(
map((result) => JSON.stringify(result)) map((result) => JSON.stringify(result))
); );
} }
@ -182,4 +180,15 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
} }
}); });
} }
showEntityNotFoundError(): void {
this.store.dispatch(new ActionNotificationShow(
{
message: this.translate.instant('edge.load-entity-error'),
type: 'error',
verticalPosition: 'top',
horizontalPosition: 'left'
}
));
}
} }

4
ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-header.component.ts

@ -35,8 +35,4 @@ export class EdgeDownlinkTableHeaderComponent extends EntityTableHeaderComponent
constructor(protected store: Store<AppState>) { constructor(protected store: Store<AppState>) {
super(store); super(store);
} }
eventTypeChanged() {
this.eventTableConfig.table.resetSortAndFilter(true, true);
}
} }

23
ui-ngx/src/app/modules/home/components/edge/edge-downlink-table.component.ts

@ -25,6 +25,8 @@ import { DialogService } from '@core/services/dialog.service';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
import { EdgeService } from '@core/http/edge.service'; import { EdgeService } from '@core/http/edge.service';
import { EntityService } from "@core/http/entity.service"; import { EntityService } from "@core/http/entity.service";
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({ @Component({
selector: 'tb-edge-downlink-table', selector: 'tb-edge-downlink-table',
@ -64,27 +66,28 @@ export class EdgeDownlinkTableComponent implements OnInit {
edgeDownlinkTableConfig: EdgeDownlinkTableConfig; edgeDownlinkTableConfig: EdgeDownlinkTableConfig;
constructor(private edgeService: EdgeService, constructor(private attributeService: AttributeService,
private entityService: EntityService, private datePipe: DatePipe,
private dialogService: DialogService, private dialogService: DialogService,
private dialog: MatDialog,
private edgeService: EdgeService,
private entityService: EntityService,
private translate: TranslateService, private translate: TranslateService,
private attributeService: AttributeService, protected store: Store<AppState>) {
private datePipe: DatePipe,
private dialog: MatDialog) {
} }
ngOnInit() { ngOnInit() {
this.dirtyValue = !this.activeValue; this.dirtyValue = !this.activeValue;
this.edgeDownlinkTableConfig = new EdgeDownlinkTableConfig( this.edgeDownlinkTableConfig = new EdgeDownlinkTableConfig(
this.edgeService,
this.entityService,
this.dialogService,
this.translate,
this.attributeService, this.attributeService,
this.datePipe, this.datePipe,
this.dialogService,
this.dialog, this.dialog,
this.edgeService,
this.entityService,
this.translate,
this.store,
this.entityIdValue this.entityIdValue
); );
} }
} }

16
ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts

@ -20,16 +20,18 @@ import {
EventEmitter, EventEmitter,
forwardRef, forwardRef,
Input, Input,
NgZone, OnChanges, NgZone,
OnChanges,
OnInit, OnInit,
Output, SimpleChanges, Output,
SimpleChanges,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { map, mergeMap, share, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -44,6 +46,7 @@ import { DeviceProfileService } from '@core/http/device-profile.service';
import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component'; import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component';
import { MatAutocomplete } from '@angular/material/autocomplete'; import { MatAutocomplete } from '@angular/material/autocomplete';
import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from './add-device-profile-dialog.component'; import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from './add-device-profile-dialog.component';
import { emptyPageData } from "@shared/models/page/page-data";
@Component({ @Component({
selector: 'tb-device-profile-autocomplete', selector: 'tb-device-profile-autocomplete',
@ -143,6 +146,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
ngOnInit() { ngOnInit() {
this.filteredDeviceProfiles = this.selectDeviceProfileFormGroup.get('deviceProfile').valueChanges this.filteredDeviceProfiles = this.selectDeviceProfileFormGroup.get('deviceProfile').valueChanges
.pipe( .pipe(
debounceTime(150),
tap((value: DeviceProfileInfo | string) => { tap((value: DeviceProfileInfo | string) => {
let modelValue: DeviceProfileInfo | null; let modelValue: DeviceProfileInfo | null;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -169,7 +173,8 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
return ''; return '';
} }
}), }),
mergeMap(name => this.fetchDeviceProfiles(name) ), distinctUntilChanged(),
switchMap(name => this.fetchDeviceProfiles(name)),
share() share()
); );
} }
@ -289,6 +294,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
direction: Direction.ASC direction: Direction.ASC
}); });
return this.deviceProfileService.getDeviceProfileInfos(pageLink, this.transportType, {ignoreLoading: true}).pipe( return this.deviceProfileService.getDeviceProfileInfos(pageLink, this.transportType, {ignoreLoading: true}).pipe(
catchError(() => of(emptyPageData<DeviceProfileInfo>())),
map(pageData => { map(pageData => {
let data = pageData.data; let data = pageData.data;
if (this.displayAllOnEmpty) { if (this.displayAllOnEmpty) {

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html

@ -22,7 +22,7 @@
[selectable]="!disabled" [selectable]="!disabled"
[removable]="!disabled" [removable]="!disabled"
(removed)="remove(objectLwm2m)"> (removed)="remove(objectLwm2m)">
{{objectLwm2m.name}} <{{objectLwm2m.keyId}}> {{objectLwm2m.name}} #{{objectLwm2m.keyId}}
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon> <mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip> </mat-chip>
<input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}" <input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}"

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html

@ -24,9 +24,9 @@
[expanded]="isExpend" [expanded]="isExpend"
[disabled]="isExpend"> [disabled]="isExpend">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title class="tb-panel-title-height" fxLayout="row"> <mat-panel-title class="tb-panel" fxLayout="row">
<div fxFlex="30" fxLayoutAlign="start center"> <div fxFlex="30" fxLayoutAlign="start center">
{{ 'device-profile.lwm2m.instance' | translate }} <<b>{{instances.get('id').value}}</b>> <span class="tb-panel-title">{{ 'device-profile.lwm2m.instance' | translate }}</span>&nbsp;#{{instances.get('id').value}}
</div> </div>
<div fxLayoutAlign="center center" fxFlex="10"> <div fxLayoutAlign="center center" fxFlex="10">
<mat-checkbox color="warn" <mat-checkbox color="warn"

6
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss

@ -14,8 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
:host{ :host{
.tb-panel-title-height { .tb-panel {
min-height: 42px; min-height: 42px;
.tb-panel-title {
font-weight: 500;
}
} }
.instance-row { .instance-row {

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts

@ -211,7 +211,7 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue
} }
getNameInstance(instance: Instance): string { getNameInstance(instance: Instance): string {
return `${this.translate.instant('device-profile.lwm2m.instance')} <${instance.id}>`; return `${this.translate.instant('device-profile.lwm2m.instance')} #${instance.id}`;
} }
disableObserveInstance = (instance: AbstractControl): boolean => { disableObserveInstance = (instance: AbstractControl): boolean => {

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html

@ -38,7 +38,7 @@
*ngFor="let resourceLwM2M of resourcesFormArray.controls; let $index = index; trackBy: trackByParams"> *ngFor="let resourceLwM2M of resourcesFormArray.controls; let $index = index; trackBy: trackByParams">
<div [formGroupName]="$index" fxLayout="row" fxLayoutAlign="start center" class="resource-list"> <div [formGroupName]="$index" fxLayout="row" fxLayoutAlign="start center" class="resource-list">
<div class="resource-name" fxFlex="30"> <div class="resource-name" fxFlex="30">
<<b>{{resourceLwM2M.get('id').value}}</b>> <b>{{resourceLwM2M.get('name').value}}</b> #{{resourceLwM2M.get('id').value}} <span class="name">{{resourceLwM2M.get('name').value}}</span>
</div> </div>
<div fxFlex="10" fxLayoutAlign="center center"> <div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="attribute" color="warn"> <mat-checkbox formControlName="attribute" color="warn">

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.scss

@ -18,6 +18,10 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
.name {
font-weight: 500;
}
} }
.checkbox-column-title { .checkbox-column-title {

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts

@ -105,6 +105,8 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue
this.resourcesFormGroup.disable({emitEvent: false}); this.resourcesFormGroup.disable({emitEvent: false});
} else { } else {
this.resourcesFormArray.controls.forEach(resource => { this.resourcesFormArray.controls.forEach(resource => {
resource.get('id').enable({emitEvent: false});
resource.get('name').enable({emitEvent: false});
resource.get('keyName').enable({emitEvent: false}); resource.get('keyName').enable({emitEvent: false});
resource.get('attribute').enable({emitEvent: false}); resource.get('attribute').enable({emitEvent: false});
resource.get('telemetry').enable({onlySelf: true}); resource.get('telemetry').enable({onlySelf: true});
@ -124,7 +126,7 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue
} }
getNameResourceLwm2m(resourceLwM2M: ResourceLwM2M): string { getNameResourceLwm2m(resourceLwM2M: ResourceLwM2M): string {
return `<${resourceLwM2M.id}> ${resourceLwM2M.name}`; return `#${resourceLwM2M.id} ${resourceLwM2M.name}`;
} }
private updatedResources(resources: ResourceLwM2M[]): void { private updatedResources(resources: ResourceLwM2M[]): void {

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html

@ -22,7 +22,7 @@
[formGroupName]="$index"> [formGroupName]="$index">
<mat-expansion-panel-header > <mat-expansion-panel-header >
<mat-panel-title fxLayoutAlign="start center" > <mat-panel-title fxLayoutAlign="start center" >
<b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;{{ objectLwM2M.get('id').value}}> <span class="tb-panel-title">{{ objectLwM2M.get('name').value}}</span>&nbsp;#{{ objectLwM2M.get('keyId').value}}
<span fxFlex></span> <span fxFlex></span>
<tb-profile-lwm2m-attributes <tb-profile-lwm2m-attributes
formControlName="attributes" formControlName="attributes"

5
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss

@ -18,8 +18,7 @@
padding: 2px; padding: 2px;
} }
.tb-panel-title-height { .tb-panel-title {
user-select: none; font-weight: 500;
min-height: 32px;
} }
} }

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

@ -264,6 +264,6 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor,
} }
getNameObject = (objectLwM2M: ObjectLwM2M): string => { getNameObject = (objectLwM2M: ObjectLwM2M): string => {
return `${objectLwM2M.name} <${objectLwM2M.id}>`; return `${objectLwM2M.name} #${objectLwM2M.keyId}`;
} }
} }

10
ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts

@ -16,10 +16,10 @@
import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { map, mergeMap, share, startWith, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -33,6 +33,7 @@ import { ENTER } from '@angular/cdk/keycodes';
import { TenantProfile } from '@shared/models/tenant.model'; import { TenantProfile } from '@shared/models/tenant.model';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { TenantProfileDialogComponent, TenantProfileDialogData } from './tenant-profile-dialog.component'; import { TenantProfileDialogComponent, TenantProfileDialogData } from './tenant-profile-dialog.component';
import { emptyPageData } from "@shared/models/page/page-data";
@Component({ @Component({
selector: 'tb-tenant-profile-autocomplete', selector: 'tb-tenant-profile-autocomplete',
@ -99,6 +100,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
ngOnInit() { ngOnInit() {
this.filteredTenantProfiles = this.selectTenantProfileFormGroup.get('tenantProfile').valueChanges this.filteredTenantProfiles = this.selectTenantProfileFormGroup.get('tenantProfile').valueChanges
.pipe( .pipe(
debounceTime(150),
tap((value: EntityInfoData | string) => { tap((value: EntityInfoData | string) => {
let modelValue: TenantProfileId | null; let modelValue: TenantProfileId | null;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -109,7 +111,8 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
this.updateView(modelValue); this.updateView(modelValue);
}), }),
map(value => value ? (typeof value === 'string' ? value : value.name) : ''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchTenantProfiles(name) ), distinctUntilChanged(),
switchMap(name => this.fetchTenantProfiles(name)),
share() share()
); );
} }
@ -174,6 +177,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
direction: Direction.ASC direction: Direction.ASC
}); });
return this.tenantProfileService.getTenantProfileInfos(pageLink, {ignoreLoading: true}).pipe( return this.tenantProfileService.getTenantProfileInfos(pageLink, {ignoreLoading: true}).pipe(
catchError(() => of(emptyPageData<EntityInfoData>())),
map(pageData => { map(pageData => {
return pageData.data; return pageData.data;
}) })

15
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts

@ -16,8 +16,8 @@
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -99,6 +99,7 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
ngOnInit() { ngOnInit() {
this.filteredRuleChains = this.selectRuleChainFormGroup.get('ruleChainId').valueChanges this.filteredRuleChains = this.selectRuleChainFormGroup.get('ruleChainId').valueChanges
.pipe( .pipe(
debounceTime(150),
tap(value => { tap(value => {
let modelValue; let modelValue;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -110,8 +111,10 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
if (value === null) { if (value === null) {
this.clear(); this.clear();
} }
}), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), }),
mergeMap(name => this.fetchRuleChain(name) ), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
distinctUntilChanged(),
switchMap(name => this.fetchRuleChain(name) ),
share() share()
); );
} }
@ -190,7 +193,9 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
this.searchText = searchText; this.searchText = searchText;
// voba: at the moment device profiles are not supported by edge, so 'core' hardcoded // voba: at the moment device profiles are not supported by edge, so 'core' hardcoded
return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText, return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText,
50, RuleChainType.CORE, {ignoreLoading: true}); 50, RuleChainType.CORE, {ignoreLoading: true}).pipe(
catchError(() => of([]))
);
} }
clear() { clear() {

2
ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts

@ -278,7 +278,7 @@ export class TbFlot {
}; };
} }
if (this.chartType === 'line' && isFinite(this.settings.thresholdsLineWidth)) { if ((this.chartType === 'line' || this.chartType === 'bar') && isFinite(this.settings.thresholdsLineWidth)) {
this.options.grid.markingsLineWidth = this.settings.thresholdsLineWidth; this.options.grid.markingsLineWidth = this.settings.thresholdsLineWidth;
} }

5
ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts

@ -413,10 +413,7 @@ export const commonMapSettingsSchema =
}, },
'mapPageSize', 'mapPageSize',
'draggableMarker', 'draggableMarker',
{ 'disableScrollZooming',
key: 'disableScrollZooming',
condition: 'model.provider !== "image-map"'
},
{ {
key: 'latKeyName', key: 'latKeyName',
condition: 'model.provider !== "image-map"' condition: 'model.provider !== "image-map"'

2
ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts

@ -384,7 +384,7 @@ export class WidgetComponentService {
} }
private createWidgetControllerDescriptor(widgetInfo: WidgetInfo, name: string): WidgetControllerDescriptor { private createWidgetControllerDescriptor(widgetInfo: WidgetInfo, name: string): WidgetControllerDescriptor {
let widgetTypeFunctionBody = `return function ${name} (ctx) {\n` + let widgetTypeFunctionBody = `return function _${name} (ctx) {\n` +
' var self = this;\n' + ' var self = this;\n' +
' self.ctx = ctx;\n\n'; /*+ ' self.ctx = ctx;\n\n'; /*+

6
ui-ngx/src/app/modules/home/components/widget/widget-config.component.html

@ -15,9 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<mat-tab-group [ngClass]="{'tb-headless': (widgetType === widgetTypes.static && !displayAdvanced())}" <mat-tab-group class="tb-widget-config tb-absolute-fill" [(selectedIndex)]="selectedTab">
class="tb-widget-config tb-absolute-fill" [(selectedIndex)]="selectedTab"> <mat-tab label="{{ 'widget-config.data' | translate }}" *ngIf="widgetType !== widgetTypes.static">
<mat-tab label="{{ 'widget-config.data' | translate }}" [fxShow]="widgetType !== widgetTypes.static">
<div [formGroup]="dataSettings" class="mat-content mat-padding" fxLayout="column" fxLayoutGap="8px"> <div [formGroup]="dataSettings" class="mat-content mat-padding" fxLayout="column" fxLayoutGap="8px">
<div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm" fxFlex="100" <div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm" fxFlex="100"
fxLayout.xs="column" fxLayoutGap="8px" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"> fxLayout.xs="column" fxLayoutGap="8px" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center">
@ -85,7 +84,6 @@
</div> </div>
<mat-expansion-panel class="tb-datasources" *ngIf="widgetType !== widgetTypes.rpc && <mat-expansion-panel class="tb-datasources" *ngIf="widgetType !== widgetTypes.rpc &&
widgetType !== widgetTypes.alarm && widgetType !== widgetTypes.alarm &&
widgetType !== widgetTypes.static &&
modelValue?.isDataEnabled" [expanded]="true"> modelValue?.isDataEnabled" [expanded]="true">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayout="column"> <mat-panel-title fxLayout="column">

2
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -38,7 +38,7 @@
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'manageCredentials')" (click)="onEntityAction($event, 'manageCredentials')"
[fxShow]="!isEdit"> [fxShow]="!isEdit">
{{ (deviceScope === 'customer_user' ? 'device.view-credentials' : 'device.manage-credentials') | translate }} {{ ((deviceScope === 'customer_user' || deviceScope === 'edge_customer_user') ? 'device.view-credentials' : 'device.manage-credentials') | translate }}
</button> </button>
<button mat-raised-button color="primary" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"

2
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -48,7 +48,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
deviceCredentials$: Subject<DeviceCredentials>; deviceCredentials$: Subject<DeviceCredentials>;
deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge'; deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge' | 'edge_customer_user';
otaUpdateType = OtaUpdateType; otaUpdateType = OtaUpdateType;

2
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

@ -528,7 +528,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
deviceId: device.id.id, deviceId: device.id.id,
isReadOnly: this.config.componentsData.deviceScope === 'customer_user' isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'
} }
}).afterClosed().subscribe(deviceCredentials => { }).afterClosed().subscribe(deviceCredentials => {
if (isDefinedAndNotNull(deviceCredentials)) { if (isDefinedAndNotNull(deviceCredentials)) {

7
ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts

@ -19,7 +19,7 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { map, mergeMap, startWith, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { DashboardInfo } from '@app/shared/models/dashboard.models'; import { DashboardInfo } from '@app/shared/models/dashboard.models';
import { DashboardService } from '@core/http/dashboard.service'; import { DashboardService } from '@core/http/dashboard.service';
@ -107,6 +107,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
ngOnInit() { ngOnInit() {
this.filteredDashboards = this.selectDashboardFormGroup.get('dashboard').valueChanges this.filteredDashboards = this.selectDashboardFormGroup.get('dashboard').valueChanges
.pipe( .pipe(
debounceTime(150),
tap(value => { tap(value => {
let modelValue; let modelValue;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -118,7 +119,8 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
}), }),
startWith<string | DashboardInfo>(''), startWith<string | DashboardInfo>(''),
map(value => value ? (typeof value === 'string' ? value : value.name) : ''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchDashboards(name) ) distinctUntilChanged(),
switchMap(name => this.fetchDashboards(name) )
); );
} }
@ -189,6 +191,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
direction: Direction.ASC direction: Direction.ASC
}); });
return this.getDashboards(pageLink).pipe( return this.getDashboards(pageLink).pipe(
catchError(() => of(emptyPageData<DashboardInfo>())),
map(pageData => { map(pageData => {
return pageData.data; return pageData.data;
}) })

9
ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts

@ -26,8 +26,8 @@ import {
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -142,6 +142,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
ngOnInit() { ngOnInit() {
this.filteredEntities = this.selectEntityFormGroup.get('entity').valueChanges this.filteredEntities = this.selectEntityFormGroup.get('entity').valueChanges
.pipe( .pipe(
debounceTime(150),
tap(value => { tap(value => {
let modelValue; let modelValue;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -156,7 +157,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
}), }),
// startWith<string | BaseData<EntityId>>(''), // startWith<string | BaseData<EntityId>>(''),
map(value => value ? (typeof value === 'string' ? value : value.name) : ''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchEntities(name) ), distinctUntilChanged(),
switchMap(name => this.fetchEntities(name)),
share() share()
); );
} }
@ -326,6 +328,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
const targetEntityType = this.checkEntityType(this.entityTypeValue); const targetEntityType = this.checkEntityType(this.entityTypeValue);
return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText,
50, this.entitySubtypeValue, {ignoreLoading: true}).pipe( 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe(
catchError(() => of(null)),
map((data) => { map((data) => {
if (data) { if (data) {
if (this.excludeEntityIds && this.excludeEntityIds.length) { if (this.excludeEntityIds && this.excludeEntityIds.length) {

18
ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts

@ -16,8 +16,17 @@
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subscription, throwError } from 'rxjs'; import { Observable, of, Subscription, throwError } from 'rxjs';
import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; import {
catchError,
debounceTime,
distinctUntilChanged,
map,
publishReplay,
refCount,
switchMap,
tap
} from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -137,12 +146,14 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.filteredSubTypes = this.subTypeFormGroup.get('subType').valueChanges this.filteredSubTypes = this.subTypeFormGroup.get('subType').valueChanges
.pipe( .pipe(
debounceTime(150),
distinctUntilChanged(),
tap(value => { tap(value => {
this.updateView(value); this.updateView(value);
}), }),
// startWith<string | EntitySubtype>(''), // startWith<string | EntitySubtype>(''),
map(value => value ? value : ''), map(value => value ? value : ''),
mergeMap(type => this.fetchSubTypes(type) ) switchMap(type => this.fetchSubTypes(type))
); );
} }
@ -221,6 +232,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
} }
if (subTypesObservable) { if (subTypesObservable) {
this.subTypes = subTypesObservable.pipe( this.subTypes = subTypesObservable.pipe(
catchError(() => of([] as Array<EntitySubtype>)),
map(subTypes => subTypes.map(subType => subType.type)), map(subTypes => subTypes.map(subType => subType.type)),
publishReplay(1), publishReplay(1),
refCount() refCount()

11
ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts

@ -16,8 +16,8 @@
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -27,11 +27,11 @@ import { EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data'; import { BaseData } from '@shared/models/base-data';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models'; import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models';
import { OtaPackageService } from '@core/http/ota-package.service'; import { OtaPackageService } from '@core/http/ota-package.service';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { emptyPageData } from "@shared/models/page/page-data";
@Component({ @Component({
selector: 'tb-ota-package-autocomplete', selector: 'tb-ota-package-autocomplete',
@ -109,6 +109,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
ngOnInit() { ngOnInit() {
this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges
.pipe( .pipe(
debounceTime(150),
tap(value => { tap(value => {
let modelValue; let modelValue;
if (typeof value === 'string' || !value) { if (typeof value === 'string' || !value) {
@ -122,7 +123,8 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
} }
}), }),
map(value => value ? (typeof value === 'string' ? value : value.title) : ''), map(value => value ? (typeof value === 'string' ? value : value.title) : ''),
mergeMap(name => this.fetchPackages(name)), distinctUntilChanged(),
switchMap(name => this.fetchPackages(name)),
share() share()
); );
} }
@ -217,6 +219,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
}); });
return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type,
{ignoreLoading: true}).pipe( {ignoreLoading: true}).pipe(
catchError(() => of(emptyPageData<OtaPackageInfo>())),
map((data) => data && data.data.length ? data.data : null) map((data) => data && data.data.length ? data.data : null)
); );
} }

18
ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts

@ -16,8 +16,17 @@
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; import {
catchError,
debounceTime,
distinctUntilChanged,
map,
publishReplay,
refCount,
switchMap,
tap
} from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -87,11 +96,13 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft
ngOnInit() { ngOnInit() {
this.filteredQueues = this.queueFormGroup.get('queue').valueChanges this.filteredQueues = this.queueFormGroup.get('queue').valueChanges
.pipe( .pipe(
debounceTime(150),
distinctUntilChanged(),
tap(value => { tap(value => {
this.updateView(value); this.updateView(value);
}), }),
map(value => value ? value : ''), map(value => value ? value : ''),
mergeMap(queue => this.fetchQueues(queue) ) switchMap(queue => this.fetchQueues(queue) )
); );
} }
@ -138,6 +149,7 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft
fetchQueues(searchText?: string): Observable<Array<string>> { fetchQueues(searchText?: string): Observable<Array<string>> {
this.searchText = searchText; this.searchText = searchText;
return this.getQueues().pipe( return this.getQueues().pipe(
catchError(() => of([])),
map(queues => { map(queues => {
const result = queues.filter( queue => { const result = queues.filter( queue => {
return searchText ? queue.toUpperCase().startsWith(searchText.toUpperCase()) : true; return searchText ? queue.toUpperCase().startsWith(searchText.toUpperCase()) : true;

2
ui-ngx/src/app/shared/components/time/timewindow-panel.component.html

@ -17,7 +17,7 @@
--> -->
<form [formGroup]="timewindowForm" (ngSubmit)="update()"> <form [formGroup]="timewindowForm" (ngSubmit)="update()">
<fieldset [disabled]="(isLoading$ | async)"> <fieldset [disabled]="(isLoading$ | async)">
<div class="mat-content" style="height: 100%;" fxLayout="column"> <div class="mat-content" fxLayout="column">
<mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}" <mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}"
(selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab"> (selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab">
<mat-tab label="{{ 'timewindow.realtime' | translate }}"> <mat-tab label="{{ 'timewindow.realtime' | translate }}">

2
ui-ngx/src/app/shared/components/time/timewindow.component.ts

@ -185,7 +185,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
}); });
if (isGtXs) { if (isGtXs) {
config.minWidth = '417px'; config.minWidth = '417px';
config.maxHeight = '500px'; config.maxHeight = '550px';
const panelHeight = 375; const panelHeight = 375;
const panelWidth = 417; const panelWidth = 417;
const el = this.timewindowPanelOrigin.elementRef.nativeElement; const el = this.timewindowPanelOrigin.elementRef.nativeElement;

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1233,7 +1233,7 @@
"instances-input": "Input Instance Id value", "instances-input": "Input Instance Id value",
"instances-input-holder": "Input Instance number...", "instances-input-holder": "Input Instance number...",
"instance": "Instance", "instance": "Instance",
"resource-label": "<id> Resource name", "resource-label": "#ID Resource name",
"observe-label": "Observe", "observe-label": "Observe",
"attribute-label": "Attribute", "attribute-label": "Attribute",
"telemetry-label": "Telemetry", "telemetry-label": "Telemetry",
@ -1450,7 +1450,7 @@
"make-private-edge-text": "After the confirmation the edge and all its data will be made private and won't be accessible by others.", "make-private-edge-text": "After the confirmation the edge and all its data will be made private and won't be accessible by others.",
"import": "Import edge", "import": "Import edge",
"label": "Label", "label": "Label",
"load-entity-error": "Failed to load data. Entity not found or has been deleted.", "load-entity-error": "Failed to load data. Entity has been deleted.",
"assign-new-edge": "Assign new edge", "assign-new-edge": "Assign new edge",
"manage-edge-dashboards": "Edge dashboards", "manage-edge-dashboards": "Edge dashboards",
"unassign-from-edge": "Unassign from edge", "unassign-from-edge": "Unassign from edge",

Loading…
Cancel
Save