Browse Source

Merge branch 'develop/3.0' of https://github.com/thingsboard/thingsboard into map/3.0

pull/2664/head
Adsumus 7 years ago
parent
commit
8460358f06
  1. 34
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  2. 30
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  3. 34
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  4. 12
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  5. 2
      msa/js-executor/package.json
  6. 2
      msa/web-ui/package.json
  7. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  8. 3506
      ui-ngx/package-lock.json
  9. 118
      ui-ngx/package.json
  10. 2
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.html
  11. 4
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.html
  12. 10
      ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.html
  13. 14
      ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.ts
  14. 2
      ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.html
  15. 23
      ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.ts
  16. 38
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.html
  17. 6
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.scss
  18. 7
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts
  19. 8
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html
  20. 8
      ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.html
  21. 16
      ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts
  22. 1
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html
  23. 8
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.scss
  24. 8
      ui-ngx/src/app/modules/home/components/dashboard/layout-button.scss
  25. 18
      ui-ngx/src/app/modules/home/components/dashboard/select-target-layout-dialog.component.html
  26. 15
      ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.html
  27. 3
      ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.ts
  28. 2
      ui-ngx/src/app/modules/home/components/details-panel.component.html
  29. 2
      ui-ngx/src/app/modules/home/components/details-panel.component.scss
  30. 6
      ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts
  31. 12
      ui-ngx/src/app/modules/home/components/entity/entity-filter.component.html
  32. 60
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html
  33. 24
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss
  34. 16
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
  35. 4
      ui-ngx/src/app/modules/home/components/relation/relation-table.component.html
  36. 2
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html
  37. 34
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts
  38. 4
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html
  39. 7
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts
  40. 16
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html
  41. 15
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.models.ts
  42. 19
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts
  43. 20
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html
  44. 20
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.scss
  45. 2
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts
  46. 10
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html
  47. 1
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.scss
  48. 21
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html
  49. 22
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.scss
  50. 5
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.scss
  51. 14
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
  52. 3
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts
  53. 1
      ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
  54. 4
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html
  55. 4
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  56. 4
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html
  57. 162
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  58. 24
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss
  59. 2
      ui-ngx/src/app/modules/home/home.component.ts
  60. 13
      ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.html
  61. 8
      ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.ts
  62. 2
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html
  63. 24
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-page.component.html
  64. 20
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.html
  65. 14
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.scss
  66. 2
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.ts
  67. 6
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-toolbar.component.html
  68. 29
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-toolbar.component.scss
  69. 2
      ui-ngx/src/app/modules/home/pages/dashboard/layout/dashboard-layout.component.ts
  70. 3
      ui-ngx/src/app/modules/home/pages/dashboard/layout/layout.models.ts
  71. 27
      ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.html
  72. 11
      ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.ts
  73. 10
      ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.html
  74. 4
      ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.ts
  75. 2
      ui-ngx/src/app/modules/home/pages/dashboard/states/entity-state-controller.component.ts
  76. 30
      ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html
  77. 8
      ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts
  78. 32
      ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html
  79. 7
      ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts
  80. 6
      ui-ngx/src/app/shared/components/color-input.component.scss
  81. 22
      ui-ngx/src/app/shared/components/color-input.component.ts
  82. 31
      ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts
  83. 2
      ui-ngx/src/app/shared/components/dashboard-select.component.html
  84. 2
      ui-ngx/src/app/shared/components/dialog/todo-dialog.component.ts
  85. 39
      ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts
  86. 11
      ui-ngx/src/app/shared/components/fab-toolbar.component.ts
  87. 1
      ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx
  88. 7
      ui-ngx/src/app/shared/components/led-light.component.ts
  89. 2
      ui-ngx/src/app/shared/components/logo.component.ts
  90. 2
      ui-ngx/src/app/shared/components/material-icon-select.component.html
  91. 6
      ui-ngx/src/app/shared/components/material-icon-select.component.scss
  92. 24
      ui-ngx/src/app/shared/components/material-icon-select.component.ts
  93. 2
      ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html
  94. 28
      ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts
  95. 8
      ui-ngx/src/app/shared/components/socialshare-panel.component.html
  96. 12
      ui-ngx/src/app/shared/components/socialshare-panel.component.ts
  97. 1
      ui-ngx/src/styles.scss
  98. 18
      ui/src/app/common/types.constant.js
  99. 7
      ui/src/app/components/gateway/gateway-config.directive.js
  100. 12
      ui/src/app/components/gateway/gateway-config.tpl.html

34
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -126,18 +126,14 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
log.trace("Executing saveAsset [{}]", asset); log.trace("Executing saveAsset [{}]", asset);
assetValidator.validate(asset, Asset::getTenantId); assetValidator.validate(asset, Asset::getTenantId);
Asset savedAsset; Asset savedAsset;
if (!sqlDatabaseUsed) { try {
savedAsset = assetDao.save(asset.getTenantId(), asset); savedAsset = assetDao.save(asset.getTenantId(), asset);
} else { } catch (Exception t) {
try { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
savedAsset = assetDao.save(asset.getTenantId(), asset); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("asset_name_unq_key")) {
} catch (Exception t) { throw new DataValidationException("Asset with such name already exists!");
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); } else {
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("asset_name_unq_key")) { throw t;
throw new DataValidationException("Asset with such name already exists!");
} else {
throw t;
}
} }
} }
return savedAsset; return savedAsset;
@ -324,26 +320,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override @Override
protected void validateCreate(TenantId tenantId, Asset asset) { protected void validateCreate(TenantId tenantId, Asset asset) {
if (!sqlDatabaseUsed) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> {
throw new DataValidationException("Asset with such name already exists!");
}
);
}
} }
@Override @Override
protected void validateUpdate(TenantId tenantId, Asset asset) { protected void validateUpdate(TenantId tenantId, Asset asset) {
if (!sqlDatabaseUsed) {
assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
d -> {
if (!d.getId().equals(asset.getId())) {
throw new DataValidationException("Asset with such name already exists!");
}
}
);
}
} }
@Override @Override

30
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -80,18 +80,14 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
} }
log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials); log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
credentialsValidator.validate(deviceCredentials, id -> tenantId); credentialsValidator.validate(deviceCredentials, id -> tenantId);
if (!sqlDatabaseUsed) { try {
return deviceCredentialsDao.save(tenantId, deviceCredentials); return deviceCredentialsDao.save(tenantId, deviceCredentials);
} else { } catch (Exception t) {
try { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
return deviceCredentialsDao.save(tenantId, deviceCredentials); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_credentials_id_unq_key")) {
} catch (Exception t) { throw new DataValidationException("Specified credentials are already registered!");
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); } else {
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_credentials_id_unq_key")) { throw t;
throw new DataValidationException("Specified credentials are already registered!");
} else {
throw t;
}
} }
} }
} }
@ -115,12 +111,6 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
@Override @Override
protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) { protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) {
if (!sqlDatabaseUsed) {
DeviceCredentials existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId());
if (existingCredentialsEntity != null) {
throw new DataValidationException("Create of existent device credentials!");
}
}
} }
@Override @Override
@ -129,12 +119,6 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
if (existingCredentials == null) { if (existingCredentials == null) {
throw new DataValidationException("Unable to update non-existent device credentials!"); throw new DataValidationException("Unable to update non-existent device credentials!");
} }
if (!sqlDatabaseUsed) {
DeviceCredentials sameCredentialsId = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId());
if (sameCredentialsId != null && !sameCredentialsId.getUuidId().equals(deviceCredentials.getUuidId())) {
throw new DataValidationException("Specified credentials are already registered!");
}
}
} }
@Override @Override

34
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -144,18 +144,14 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
log.trace("Executing saveDevice [{}]", device); log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device, Device::getTenantId); deviceValidator.validate(device, Device::getTenantId);
Device savedDevice; Device savedDevice;
if (!sqlDatabaseUsed) { try {
savedDevice = deviceDao.save(device.getTenantId(), device); savedDevice = deviceDao.save(device.getTenantId(), device);
} else { } catch (Exception t) {
try { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
savedDevice = deviceDao.save(device.getTenantId(), device); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) {
} catch (Exception t) { throw new DataValidationException("Device with such name already exists!");
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); } else {
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) { throw t;
throw new DataValidationException("Device with such name already exists!");
} else {
throw t;
}
} }
} }
if (device.getId() == null) { if (device.getId() == null) {
@ -362,26 +358,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override @Override
protected void validateCreate(TenantId tenantId, Device device) { protected void validateCreate(TenantId tenantId, Device device) {
if (!sqlDatabaseUsed) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
throw new DataValidationException("Device with such name already exists!");
}
);
}
} }
@Override @Override
protected void validateUpdate(TenantId tenantId, Device device) { protected void validateUpdate(TenantId tenantId, Device device) {
if (!sqlDatabaseUsed) {
deviceDao.findDeviceByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
if (!d.getUuidId().equals(device.getUuidId())) {
throw new DataValidationException("Device with such name already exists!");
}
}
);
}
} }
@Override @Override

12
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -18,12 +18,10 @@ package org.thingsboard.server.dao.entity;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import javax.annotation.PostConstruct;
import java.util.Optional; import java.util.Optional;
@Slf4j @Slf4j
@ -32,16 +30,6 @@ public abstract class AbstractEntityService {
@Autowired @Autowired
protected RelationService relationService; protected RelationService relationService;
@Value("${database.entities.type:sql}")
private String databaseType;
protected boolean sqlDatabaseUsed;
@PostConstruct
public void init() {
sqlDatabaseUsed = "sql".equalsIgnoreCase(databaseType);
}
protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) { protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
log.trace("Executing deleteEntityRelations [{}]", entityId); log.trace("Executing deleteEntityRelations [{}]", entityId);
relationService.deleteEntityRelations(tenantId, entityId); relationService.deleteEntityRelations(tenantId, entityId);

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-js-executor", "name": "thingsboard-js-executor",
"private": true, "private": true,
"version": "2.4.3", "version": "3.0.0",
"description": "ThingsBoard JavaScript Executor Microservice", "description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.js", "main": "server.js",
"bin": "server.js", "bin": "server.js",

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-web-ui", "name": "thingsboard-web-ui",
"private": true, "private": true,
"version": "2.4.3", "version": "3.0.0",
"description": "ThingsBoard Web UI Microservice", "description": "ThingsBoard Web UI Microservice",
"main": "server.js", "main": "server.js",
"bin": "server.js", "bin": "server.js",

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

3506
ui-ngx/package-lock.json

File diff suppressed because it is too large

118
ui-ngx/package.json

@ -13,122 +13,122 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^9.0.7", "@angular/animations": "^9.1.2",
"@angular/cdk": "^9.1.3", "@angular/cdk": "^9.1.3",
"@angular/common": "^9.0.7", "@angular/common": "^9.1.2",
"@angular/compiler": "^9.0.7", "@angular/compiler": "^9.1.2",
"@angular/core": "^9.0.7", "@angular/core": "^9.1.2",
"@angular/flex-layout": "^9.0.0-beta.29", "@angular/flex-layout": "^9.0.0-beta.29",
"@angular/forms": "^9.0.7", "@angular/forms": "^9.1.2",
"@angular/material": "^9.1.3", "@angular/material": "^9.2.1",
"@angular/platform-browser": "^9.0.7", "@angular/platform-browser": "^9.1.2",
"@angular/platform-browser-dynamic": "^9.0.7", "@angular/platform-browser-dynamic": "^9.1.2",
"@angular/router": "^9.0.7", "@angular/router": "^9.1.2",
"@auth0/angular-jwt": "^4.0.0", "@auth0/angular-jwt": "^4.0.0",
"@date-io/date-fns": "^2.4.0", "@date-io/date-fns": "^2.6.1",
"@flowjs/flow.js": "^2.14.0", "@flowjs/flow.js": "^2.14.0",
"@flowjs/ngx-flow": "^0.4.3", "@flowjs/ngx-flow": "^0.4.3",
"@mat-datetimepicker/core": "^4.0.1", "@mat-datetimepicker/core": "^4.1.0",
"@material-ui/core": "^4.9.3", "@material-ui/core": "^4.9.11",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@material-ui/pickers": "^3.2.10", "@material-ui/pickers": "^3.2.10",
"@ngrx/effects": "^8.6.0", "@ngrx/effects": "^9.1.0",
"@ngrx/store": "^8.6.0", "@ngrx/store": "^9.1.0",
"@ngrx/store-devtools": "^8.6.0", "@ngrx/store-devtools": "^9.1.0",
"@ngx-share/core": "^7.1.4", "@ngx-share/core": "^7.1.4",
"@ngx-translate/core": "^12.1.1", "@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0", "@ngx-translate/http-loader": "^4.0.0",
"ace-builds": "^1.4.8", "ace-builds": "^1.4.11",
"angular-gridster2": "^9.0.1", "angular-gridster2": "^9.1.0",
"angular2-hotkeys": "^2.1.5", "angular2-hotkeys": "^2.2.0",
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"canvas-gauges": "^2.1.5", "canvas-gauges": "^2.1.7",
"compass-sass-mixins": "^0.12.7", "compass-sass-mixins": "^0.12.7",
"core-js": "^3.6.4", "core-js": "^3.6.5",
"date-fns": "^2.9.0", "date-fns": "^2.12.0",
"flot": "git://github.com/thingsboard/flot.git#0.9-work", "flot": "git://github.com/thingsboard/flot.git#0.9-work",
"flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", "flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"javascript-detect-element-resize": "^0.5.3", "javascript-detect-element-resize": "^0.5.3",
"jquery": "^3.4.1", "jquery": "^3.5.0",
"jquery.terminal": "^2.14.0", "jquery.terminal": "^2.15.4",
"js-beautify": "^1.10.3", "js-beautify": "^1.11.0",
"json-schema-defaults": "^0.4.0", "json-schema-defaults": "^0.4.0",
"jstree": "^3.3.9", "jstree": "^3.3.9",
"jstree-bootstrap-theme": "^1.0.1", "jstree-bootstrap-theme": "^1.0.1",
"jszip": "^3.2.2", "jszip": "^3.4.0",
"leaflet": "^1.6.0", "leaflet": "^1.6.0",
"leaflet-geometryutil": "^0.9.3", "leaflet-geometryutil": "^0.9.3",
"leaflet-polylinedecorator": "^1.6.0", "leaflet-polylinedecorator": "^1.6.0",
"leaflet-providers": "^1.9.1", "leaflet-providers": "^1.9.1",
"leaflet.gridlayer.googlemutant": "^0.8.0", "leaflet.gridlayer.googlemutant": "^0.9.0",
"leaflet.markercluster": "^1.4.1", "leaflet.markercluster": "^1.4.1",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"messageformat": "^2.3.0", "messageformat": "^2.3.0",
"moment": "^2.24.0", "moment": "^2.24.0",
"ngx-clipboard": "^12.3.1", "ngx-clipboard": "^13.0.0",
"ngx-color-picker": "^9.0.0", "ngx-color-picker": "^9.1.0",
"ngx-daterangepicker-material": "^2.2.0", "ngx-daterangepicker-material": "^3.0.1",
"ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master", "ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master",
"ngx-hm-carousel": "^2.0.0-rc.1", "ngx-hm-carousel": "^2.0.0-rc.1",
"ngx-translate-messageformat-compiler": "^4.5.0", "ngx-translate-messageformat-compiler": "^4.6.0",
"objectpath": "^2.0.0", "objectpath": "^2.0.0",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"raphael": "^2.3.0", "raphael": "^2.3.0",
"rc-select": "^10.0.0-rc.6", "rc-select": "^10.2.4",
"react": "^16.12.0", "react": "^16.13.1",
"react-ace": "^8.0.0", "react-ace": "^8.1.0",
"react-dom": "^16.12.0", "react-dom": "^16.13.1",
"react-dropzone": "^10.2.1", "react-dropzone": "^10.2.2",
"reactcss": "^1.2.3", "reactcss": "^1.2.3",
"rxjs": "^6.5.4", "rxjs": "^6.5.5",
"schema-inspector": "1.6.8", "schema-inspector": "1.6.8",
"screenfull": "^5.0.2", "screenfull": "^5.0.2",
"split.js": "^1.5.11", "split.js": "^1.5.11",
"systemjs": "0.21.5", "systemjs": "0.21.5",
"tinycolor2": "^1.4.1", "tinycolor2": "^1.4.1",
"tooltipster": "^4.2.7", "tooltipster": "^4.2.7",
"tslib": "^1.10.0", "tslib": "^1.11.1",
"tv4": "^1.3.0", "tv4": "^1.3.0",
"typeface-roboto": "^0.0.75", "typeface-roboto": "^0.0.75",
"zone.js": "~0.10.2" "zone.js": "~0.10.3"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "^9.0.0-beta.7", "@angular-builders/custom-webpack": "^9.1.0",
"@angular-devkit/build-angular": "^0.900.7", "@angular-devkit/build-angular": "^0.901.1",
"@angular/cli": "^9.0.7", "@angular/cli": "^9.1.1",
"@angular/compiler-cli": "^9.0.7", "@angular/compiler-cli": "^9.1.2",
"@angular/language-service": "^9.0.7", "@angular/language-service": "^9.1.2",
"@types/canvas-gauges": "^2.1.2", "@types/canvas-gauges": "^2.1.2",
"@types/flot": "^0.0.31", "@types/flot": "^0.0.31",
"@types/jasmine": "^3.5.5", "@types/jasmine": "^3.5.10",
"@types/jasminewd2": "^2.0.8", "@types/jasminewd2": "^2.0.8",
"@types/jquery": "^3.3.32", "@types/jquery": "^3.3.35",
"@types/js-beautify": "^1.8.1", "@types/js-beautify": "^1.8.2",
"@types/jstree": "^3.3.39", "@types/jstree": "^3.3.39",
"@types/jszip": "^3.1.7", "@types/jszip": "^3.1.7",
"@types/leaflet": "^1.5.9", "@types/leaflet": "^1.5.12",
"@types/leaflet-polylinedecorator": "^1.6.0", "@types/leaflet-polylinedecorator": "^1.6.0",
"@types/lodash": "^4.14.149", "@types/lodash": "^4.14.150",
"@types/raphael": "^2.1.30", "@types/raphael": "^2.3.0",
"@types/react": "^16.9.20", "@types/react": "^16.9.34",
"@types/react-dom": "^16.9.5", "@types/react-dom": "^16.9.6",
"@types/tinycolor2": "^1.4.2", "@types/tinycolor2": "^1.4.2",
"@types/tooltipster": "^0.0.29", "@types/tooltipster": "^0.0.29",
"codelyzer": "^5.2.1", "codelyzer": "^5.2.2",
"compression-webpack-plugin": "^3.1.0", "compression-webpack-plugin": "^3.1.0",
"directory-tree": "^2.2.4", "directory-tree": "^2.2.4",
"jasmine-core": "^3.5.0", "jasmine-core": "^3.5.0",
"jasmine-spec-reporter": "^4.2.1", "jasmine-spec-reporter": "^5.0.1",
"karma": "^4.4.1", "karma": "^5.0.2",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.1.1", "karma-coverage-istanbul-reporter": "^2.1.1",
"karma-jasmine": "^3.1.1", "karma-jasmine": "^3.1.1",
"karma-jasmine-html-reporter": "^1.5.2", "karma-jasmine-html-reporter": "^1.5.3",
"ngrx-store-freeze": "^0.2.4", "ngrx-store-freeze": "^0.2.4",
"protractor": "^5.4.3", "protractor": "^5.4.4",
"ts-node": "^8.6.2", "ts-node": "^8.9.0",
"tslint": "^6.0.0", "tslint": "^6.1.1",
"typescript": "~3.7.5" "typescript": "~3.7.5"
} }
} }

2
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.html

@ -19,7 +19,7 @@
<div fxLayout="row" *ngFor="let alias of entityAliasesInfo | keyvalue"> <div fxLayout="row" *ngFor="let alias of entityAliasesInfo | keyvalue">
<mat-form-field> <mat-form-field>
<mat-label>{{alias.value.alias}}</mat-label> <mat-label>{{alias.value.alias}}</mat-label>
<mat-select matInput [(ngModel)]="alias.value.selectedId" <mat-select [(ngModel)]="alias.value.selectedId"
(ngModelChange)="currentAliasEntityChanged(alias.key, alias.value.selectedId)"> (ngModelChange)="currentAliasEntityChanged(alias.key, alias.value.selectedId)">
<mat-option *ngFor="let resolvedEntity of alias.value.resolvedEntities" [value]="resolvedEntity.id"> <mat-option *ngFor="let resolvedEntity of alias.value.resolvedEntities" [value]="resolvedEntity.id">
{{resolvedEntity.name}} {{resolvedEntity.name}}

4
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.html

@ -16,14 +16,14 @@
--> -->
<section class="tb-aliases-entity-select" fxLayout="row" fxLayoutAlign="start center"> <section class="tb-aliases-entity-select" fxLayout="row" fxLayoutAlign="start center">
<button mat-button mat-icon-button <button mat-icon-button
cdkOverlayOrigin #aliasEntitySelectPanelOrigin="cdkOverlayOrigin" cdkOverlayOrigin #aliasEntitySelectPanelOrigin="cdkOverlayOrigin"
(click)="openEditMode()" (click)="openEditMode()"
matTooltip="{{ 'entity.select-entities' | translate }}" matTooltip="{{ 'entity.select-entities' | translate }}"
[matTooltipPosition]="tooltipPosition"> [matTooltipPosition]="tooltipPosition">
<mat-icon class="material-icons">devices_other</mat-icon> <mat-icon class="material-icons">devices_other</mat-icon>
</button> </button>
<span fxHide.xs fxHide.sm fxHide.md <span fxHide.lt-lg
(click)="openEditMode()" (click)="openEditMode()"
matTooltip="{{ 'entity.select-entities' | translate }}" matTooltip="{{ 'entity.select-entities' | translate }}"
[matTooltipPosition]="tooltipPosition"> [matTooltipPosition]="tooltipPosition">

10
ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.html

@ -15,11 +15,11 @@
limitations under the License. limitations under the License.
--> -->
<form #entityAliasForm="ngForm" [formGroup]="entityAliasFormGroup" (ngSubmit)="save()" style="min-width: 550px;"> <form #entityAliasForm="ngForm" [formGroup]="entityAliasFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2>{{ (isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2> <h2>{{ (isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
@ -58,15 +58,13 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty"> [disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }} {{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

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

@ -20,26 +20,22 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { import {
AbstractControl,
FormArray,
FormBuilder, FormBuilder,
FormControl, FormControl,
FormGroup, FormGroup,
FormGroupDirective, FormGroupDirective,
NgForm, Validators, ValidatorFn NgForm,
ValidatorFn,
Validators
} from '@angular/forms'; } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { AttributeData } from '@shared/models/telemetry/telemetry.models'; import { EntityAlias, EntityAliases } from '@shared/models/alias.models';
import { EntityAlias, EntityAliases, EntityAliasFilter } from '@shared/models/alias.models';
import { DatasourceType, Widget, widgetType } from '@shared/models/widget.models';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { DialogService } from '@core/services/dialog.service';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { Observable, of } from 'rxjs'; import { Observable } from 'rxjs';
export interface EntityAliasDialogData { export interface EntityAliasDialogData {
isAdd: boolean; isAdd: boolean;

2
ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.html

@ -27,7 +27,7 @@
[matAutocomplete]="entityAliasAutocomplete"> [matAutocomplete]="entityAliasAutocomplete">
<button *ngIf="selectEntityAliasFormGroup.get('entityAlias').value && !disabled" <button *ngIf="selectEntityAliasFormGroup.get('entityAlias').value && !disabled"
type="button" type="button"
matSuffix mat-button mat-icon-button aria-label="Clear" matSuffix mat-icon-button aria-label="Clear"
(click)="clear()"> (click)="clear()">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>

23
ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.ts

@ -21,24 +21,23 @@ import {
FormControl, FormControl,
FormGroup, FormGroup,
FormGroupDirective, FormGroupDirective,
NG_VALUE_ACCESSOR, NgForm NG_VALUE_ACCESSOR,
NgForm
} from '@angular/forms'; } from '@angular/forms';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, mergeMap, startWith, tap, share, filter } from 'rxjs/operators'; import { map, mergeMap, share, 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';
import {AliasEntityType, EntityType} from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import {BaseData} from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service';
import {EntityId} from '@shared/models/id/entity-id'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {EntityService} from '@core/http/entity.service';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { EntityAlias } from '@shared/models/alias.models'; import { EntityAlias } from '@shared/models/alias.models';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { EntityAliasSelectCallbacks } from './entity-alias-select.component.models'; import { EntityAliasSelectCallbacks } from './entity-alias-select.component.models';
import {ENTER} from '@angular/cdk/keycodes'; import { ENTER } from '@angular/cdk/keycodes';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
@Component({ @Component({

38
ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.html

@ -16,10 +16,10 @@
--> -->
<form #entityAliasesForm="ngForm" [formGroup]="entityAliasesFormGroup" (ngSubmit)="save()" style="width: 700px;"> <form #entityAliasesForm="ngForm" [formGroup]="entityAliasesFormGroup" (ngSubmit)="save()" style="width: 700px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2>{{ title | translate }}</h2> <h2>{{ title | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
@ -28,18 +28,18 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar> </mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div class="tb-aliases-header" fxFlex fxLayout="row" fxLayoutAlign="start center">
<span fxFlex="5"></span>
<div fxFlex="95" fxLayout="row" fxLayoutAlign="start center">
<span class="tb-header-label" translate fxFlex="150px">alias.name</span>
<span class="tb-header-label" translate fxFlex style="padding-left: 10px;">alias.entity-filter</span>
<span class="tb-header-label" translate fxFlex="120px" style="padding-left: 10px;">alias.resolve-multiple</span>
<span style="min-width: 80px;"></span>
</div>
</div>
<mat-divider></mat-divider>
<div mat-dialog-content> <div mat-dialog-content>
<div class="tb-aliases-header" fxLayout="row" fxLayoutAlign="start center">
<span fxFlex="5"></span>
<div fxFlex="95" fxLayout="row" fxLayoutAlign="start center">
<span class="tb-header-label" translate fxFlex="150px">alias.name</span>
<span class="tb-header-label" translate fxFlex style="padding-left: 10px;">alias.entity-filter</span>
<span class="tb-header-label" translate fxFlex="120px" style="padding-left: 10px;">alias.resolve-multiple</span>
<span style="min-width: 80px;"></span>
</div>
</div>
<fieldset [disabled]="isLoading$ | async"> <fieldset [disabled]="isLoading$ | async">
<mat-divider></mat-divider>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" <div fxFlex fxLayout="row" fxLayoutAlign="start center"
formArrayName="entityAliases" formArrayName="entityAliases"
*ngFor="let entityAliasControl of entityAliasesFormArray().controls; let $index = index"> *ngFor="let entityAliasControl of entityAliasesFormArray().controls; let $index = index">
@ -63,7 +63,7 @@
</mat-slide-toggle> </mat-slide-toggle>
</section> </section>
<button [disabled]="isLoading$ | async" <button [disabled]="isLoading$ | async"
mat-button mat-icon-button color="primary" mat-icon-button color="primary"
style="min-width: 40px;" style="min-width: 40px;"
type="button" type="button"
(click)="editAlias($index)" (click)="editAlias($index)"
@ -72,7 +72,7 @@
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button [disabled]="isLoading$ | async" <button [disabled]="isLoading$ | async"
mat-button mat-icon-button color="primary" mat-icon-button color="primary"
style="min-width: 40px;" style="min-width: 40px;"
type="button" type="button"
(click)="removeAlias($index)" (click)="removeAlias($index)"
@ -84,8 +84,8 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions>
<button mat-button mat-raised-button color="primary" <button mat-raised-button color="primary"
type="button" type="button"
(click)="addAlias()" (click)="addAlias()"
[fxShow]="!disableAdd" [fxShow]="!disableAdd"
@ -95,16 +95,16 @@
{{ 'alias.add' | translate }} {{ 'alias.add' | translate }}
</button> </button>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary" <button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty"> [disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()"
cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
</div> </div>

6
ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.scss

@ -17,7 +17,7 @@
.tb-aliases-header { .tb-aliases-header {
min-height: 40px; min-height: 40px;
padding: 0 34px 0 34px; padding: 0 11px;
margin: 5px; margin: 5px;
.tb-header-label { .tb-header-label {
@ -26,6 +26,10 @@
} }
} }
mat-divider{
margin: -1px -24px;
}
.tb-alias { .tb-alias {
padding: 0 0 0 10px; padding: 0 0 0 10px;
margin: 5px; margin: 5px;

7
ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts

@ -16,7 +16,7 @@
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { import {
@ -26,11 +26,11 @@ import {
FormControl, FormControl,
FormGroup, FormGroup,
FormGroupDirective, FormGroupDirective,
NgForm, Validators NgForm,
Validators
} from '@angular/forms'; } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { AttributeData } from '@shared/models/telemetry/telemetry.models';
import { EntityAlias, EntityAliases, EntityAliasFilter } from '@shared/models/alias.models'; import { EntityAlias, EntityAliases, EntityAliasFilter } from '@shared/models/alias.models';
import { DatasourceType, Widget, widgetType } from '@shared/models/widget.models'; import { DatasourceType, Widget, widgetType } from '@shared/models/widget.models';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
@ -39,7 +39,6 @@ import { TranslateService } from '@ngx-translate/core';
import { ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationShow } from '@core/notification/notification.actions';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { deepClone, isUndefined } from '@core/utils'; import { deepClone, isUndefined } from '@core/utils';
import { MatDialog } from '@angular/material/dialog';
import { EntityAliasDialogComponent, EntityAliasDialogData } from './entity-alias-dialog.component'; import { EntityAliasDialogComponent, EntityAliasDialogData } from './entity-alias-dialog.component';
export interface EntityAliasesDialogData { export interface EntityAliasesDialogData {

8
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html

@ -16,7 +16,7 @@
--> -->
<form [formGroup]="attributeFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> <form [formGroup]="attributeFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2>{{ 'attribute.add' | translate }}</h2> <h2>{{ 'attribute.add' | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -43,15 +43,13 @@
</tb-value-input> </tb-value-input>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty"> [disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

8
ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.html

@ -16,7 +16,7 @@
--> -->
<form #addWidgetForm="ngForm" [formGroup]="addWidgetFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> <form #addWidgetForm="ngForm" [formGroup]="addWidgetFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>attribute.add-widget-to-dashboard</h2> <h2 translate>attribute.add-widget-to-dashboard</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -55,19 +55,17 @@
</mat-radio-group> </mat-radio-group>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span>
<mat-checkbox formControlName="openDashboard" <mat-checkbox formControlName="openDashboard"
style="margin-bottom: 0px; padding-right: 20px;"> style="margin-bottom: 0px; padding-right: 20px;">
{{ 'dashboard.open-dashboard' | translate }} {{ 'dashboard.open-dashboard' | translate }}
</mat-checkbox> </mat-checkbox>
<button mat-button mat-raised-button color="primary" <button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty"> [disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

16
ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts

@ -16,34 +16,26 @@
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
import { import { objToBase64 } from '@core/utils';
DashboardLayoutId,
DashboardSettings,
GridSettings,
StateControllerId,
Dashboard
} from '@app/shared/models/dashboard.models';
import { isUndefined, objToBase64 } from '@core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityId } from '@app/shared/models/id/entity-id'; import { EntityId } from '@app/shared/models/id/entity-id';
import { Widget } from '@app/shared/models/widget.models'; import { Widget } from '@app/shared/models/widget.models';
import { DashboardService } from '@core/http/dashboard.service'; import { DashboardService } from '@core/http/dashboard.service';
import { forkJoin, Observable, of } from 'rxjs'; import { forkJoin, Observable, of } from 'rxjs';
import { SelectTargetLayoutDialogComponent } from '@home/components/dashboard/select-target-layout-dialog.component'; import { SelectTargetLayoutDialogComponent } from '@home/components/dashboard/select-target-layout-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { import {
SelectTargetStateDialogComponent, SelectTargetStateDialogComponent,
SelectTargetStateDialogData SelectTargetStateDialogData
} from '@home/components/dashboard/select-target-state-dialog.component'; } from '@home/components/dashboard/select-target-state-dialog.component';
import { mergeMap, map } from 'rxjs/operators'; import { mergeMap } from 'rxjs/operators';
import { AliasesInfo } from '@shared/models/alias.models'; import { AliasesInfo } from '@shared/models/alias.models';
import { ItemBufferService } from '@core/services/item-buffer.service'; import { ItemBufferService } from '@core/services/item-buffer.service';
import { StateObject } from '@core/api/widget-api.models'; import { StateObject } from '@core/api/widget-api.models';

1
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html

@ -83,6 +83,7 @@
<div fxLayout="row" fxLayoutAlign="space-between start"> <div fxLayout="row" fxLayoutAlign="space-between start">
<div class="tb-widget-title" fxLayout="column" fxLayoutAlign="center start" [fxShow]="widget.showWidgetTitlePanel"> <div class="tb-widget-title" fxLayout="column" fxLayoutAlign="center start" [fxShow]="widget.showWidgetTitlePanel">
<span [fxShow]="widget.showTitle" <span [fxShow]="widget.showTitle"
[ngClass]="{'single-row': widget.hasTimewindow}"
[ngStyle]="widget.titleStyle" [ngStyle]="widget.titleStyle"
[matTooltip]="widget.titleTooltip" [matTooltip]="widget.titleTooltip"
matTooltipClass="tb-tooltip-multiline" matTooltipClass="tb-tooltip-multiline"

8
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.scss

@ -76,6 +76,7 @@ div.tb-widget {
tb-timewindow { tb-timewindow {
font-size: 14px; font-size: 14px;
opacity: .85; opacity: .85;
margin: 0;
} }
.title { .title {
@ -85,6 +86,13 @@ div.tb-widget {
line-height: 24px; line-height: 24px;
letter-spacing: .01em; letter-spacing: .01em;
margin: 0; margin: 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
&.single-row{
-webkit-line-clamp: 1;
}
} }
} }

8
ui-ngx/src/app/modules/home/components/dashboard/layout-button.scss

@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
:host ::ng-deep { :host {
button.tb-layout-button { button.tb-layout-button {
width: 100%; width: 100%;
max-width: 240px;
height: 100%; height: 100%;
padding: 40px 20px;
}
&::ng-deep {
.mat-button-wrapper { .mat-button-wrapper {
padding: 40px;
line-height: 18px; line-height: 18px;
span { span {
font-size: 18px; font-size: 18px;

18
ui-ngx/src/app/modules/home/components/dashboard/select-target-layout-dialog.component.html

@ -16,10 +16,10 @@
--> -->
<form #widgetForm="ngForm"> <form #widgetForm="ngForm">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>layout.select</h2> <h2 translate>layout.select</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
@ -30,16 +30,18 @@
<div mat-dialog-content> <div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="8px"> <fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="8px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<button fxFlex fxLayout="column" <button fxFlex
type="button" mat-button mat-raised-button color="primary" mat-raised-button color="primary"
type="button"
class="tb-layout-button" class="tb-layout-button"
(click)="selectLayout('main')"> (click)="selectLayout('main')">
<span translate>layout.main</span> <span translate>layout.main</span>
</button> </button>
<button fxFlex fxLayout="column" <button fxFlex
type="button" mat-button mat-raised-button color="primary" mat-raised-button color="primary"
type="button"
class="tb-layout-button" class="tb-layout-button"
(click)="selectLayout('right')"> (click)="selectLayout('right')">
<span translate>layout.right</span> <span translate>layout.right</span>
</button> </button>
</div> </div>

15
ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.html

@ -16,10 +16,10 @@
--> -->
<form #stateForm="ngForm" [formGroup]="stateFormGroup" (ngSubmit)="save()"> <form #stateForm="ngForm" [formGroup]="stateFormGroup" (ngSubmit)="save()">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>dashboard.select-state</h2> <h2 translate>dashboard.select-state</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
@ -31,7 +31,7 @@
<fieldset [disabled]="isLoading$ | async" fxLayout="column"> <fieldset [disabled]="isLoading$ | async" fxLayout="column">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>dashboard.state</mat-label> <mat-label translate>dashboard.state</mat-label>
<mat-select required matInput formControlName="stateId"> <mat-select required formControlName="stateId">
<mat-option *ngFor="let stateItem of states | keyvalue" [value]="stateItem.key"> <mat-option *ngFor="let stateItem of states | keyvalue" [value]="stateItem.key">
{{ stateItem.value.name }} {{ stateItem.value.name }}
</mat-option> </mat-option>
@ -39,18 +39,17 @@
</mat-form-field> </mat-form-field>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid"> [disabled]="(isLoading$ | async) || stateFormGroup.invalid">
{{ 'action.select' | translate }} {{ 'action.select' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()"
cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
</div> </div>

3
ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.ts

@ -22,11 +22,8 @@ import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; 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 { DashboardState } from '@app/shared/models/dashboard.models';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { DashboardService } from '@core/http/dashboard.service';
export interface SelectTargetStateDialogData { export interface SelectTargetStateDialogData {
states: {[id: string]: DashboardState }; states: {[id: string]: DashboardState };

2
ui-ngx/src/app/modules/home/components/details-panel.component.html

@ -17,7 +17,7 @@
--> -->
<header> <header>
<mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}"> <mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}">
<div class="mat-toolbar-tools" fxFlex fxLayout="row" fxLayoutAlign="start center" style="height: 100%;"> <div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center" style="height: 100%;">
<div class="mat-toolbar-tools tb-details-title-header" fxFlex fxLayout="column" fxLayoutAlign="start start"> <div class="mat-toolbar-tools tb-details-title-header" fxFlex fxLayout="column" fxLayoutAlign="start start">
<span class="tb-details-title">{{ headerTitle }}</span> <span class="tb-details-title">{{ headerTitle }}</span>
<span class="tb-details-subtitle">{{ headerSubtitle }}</span> <span class="tb-details-subtitle">{{ headerSubtitle }}</span>

2
ui-ngx/src/app/modules/home/components/details-panel.component.scss

@ -45,7 +45,7 @@
white-space: nowrap; white-space: nowrap;
@media #{$mat-gt-sm} { @media #{$mat-gt-sm} {
font-size: 1.6rem; font-size: 1.5rem;
} }
} }

6
ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts

@ -102,14 +102,14 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
} }
break; break;
case AliasFilterType.entityViewType: case AliasFilterType.entityViewType:
const entityViewType = this.filter.entityViewType; const entityView = this.filter.entityViewType;
prefix = this.filter.entityViewNameFilter; prefix = this.filter.entityViewNameFilter;
if (prefix && prefix.length) { if (prefix && prefix.length) {
this.filterDisplayValue = this.translate.instant('alias.filter-type-entity-view-type-and-name-description', this.filterDisplayValue = this.translate.instant('alias.filter-type-entity-view-type-and-name-description',
{entityViewType, prefix}); {entityView, prefix});
} else { } else {
this.filterDisplayValue = this.translate.instant('alias.filter-type-entity-view-type-description', this.filterDisplayValue = this.translate.instant('alias.filter-type-entity-view-type-description',
{entityViewType}); {entityView});
} }
break; break;
case AliasFilterType.relationsQuery: case AliasFilterType.relationsQuery:

12
ui-ngx/src/app/modules/home/components/entity/entity-filter.component.html

@ -141,10 +141,10 @@
<label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label> <label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label>
</section> </section>
</div> </div>
<div fxFlex fxLayoutGap="8px" fxLayout="row"> <div fxFlex fxLayoutGap="8px" fxLayout="row" fxLayout.xs="column">
<mat-form-field class="mat-block" style="min-width: 100px;"> <mat-form-field class="mat-block" style="min-width: 100px;">
<mat-label translate>relation.direction</mat-label> <mat-label translate>relation.direction</mat-label>
<mat-select required matInput formControlName="direction"> <mat-select required formControlName="direction">
<mat-option *ngFor="let type of directionTypes" [value]="type"> <mat-option *ngFor="let type of directionTypes" [value]="type">
{{ directionTypeTranslations.get(directionTypeEnum[type]) | translate }} {{ directionTypeTranslations.get(directionTypeEnum[type]) | translate }}
</mat-option> </mat-option>
@ -172,7 +172,7 @@
entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ? entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ?
entityFilterFormGroup.get('type').value : ''"> entityFilterFormGroup.get('type').value : ''">
<label class="tb-small">{{ 'alias.root-entity' | translate }}</label> <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0px;"> <section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
<mat-slide-toggle class="root-state-entity-switch" <mat-slide-toggle class="root-state-entity-switch"
formControlName="rootStateEntity"> formControlName="rootStateEntity">
</mat-slide-toggle> </mat-slide-toggle>
@ -200,17 +200,17 @@
</div> </div>
</div> </div>
<div fxFlex fxLayout="row"> <div fxFlex fxLayout="row">
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0px;"> <section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
<mat-slide-toggle class="root-state-entity-switch" <mat-slide-toggle class="root-state-entity-switch"
formControlName="fetchLastLevelOnly"> formControlName="fetchLastLevelOnly">
</mat-slide-toggle> </mat-slide-toggle>
<label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label> <label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label>
</section> </section>
</div> </div>
<div fxFlex fxLayoutGap="8px" fxLayout="row"> <div fxFlex fxLayoutGap="8px" fxLayout="row" fxLayout.xs="column">
<mat-form-field class="mat-block" style="min-width: 100px;"> <mat-form-field class="mat-block" style="min-width: 100px;">
<mat-label translate>relation.direction</mat-label> <mat-label translate>relation.direction</mat-label>
<mat-select required matInput formControlName="direction"> <mat-select required formControlName="direction">
<mat-option *ngFor="let type of directionTypes" [value]="type"> <mat-option *ngFor="let type of directionTypes" [value]="type">
{{ directionTypeTranslations.get(directionTypeEnum[type]) | translate }} {{ directionTypeTranslations.get(directionTypeEnum[type]) | translate }}
</mat-option> </mat-option>

60
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html

@ -16,35 +16,37 @@
--> -->
<div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup"> <div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup">
<div class="header" [fxShow]="relationFiltersFormArray().length"> <div class="container">
<div fxLayout="row" fxLayoutAlign="start center"> <div class="header" [fxShow]="relationFiltersFormArray().length">
<span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span> <div fxLayout="row" fxLayoutAlign="start center">
<span class="cell" fxFlex translate>entity.entity-types</span> <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span>
<span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span> <span class="cell" fxFlex translate>entity.entity-types</span>
<span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span>
</div>
</div> </div>
</div> <div class="body" [fxShow]="relationFiltersFormArray().length">
<div class="body" [fxShow]="relationFiltersFormArray().length"> <div class="row" fxFlex fxLayout="row"
<div class="row" fxFlex fxLayout="row" fxLayoutAlign="start center" formArrayName="relationFilters"
fxLayoutAlign="start center" formArrayName="relationFilters" *ngFor="let relationFilterControl of relationFiltersFormArray().controls; let $index = index">
*ngFor="let relationFilterControl of relationFiltersFormArray().controls; let $index = index"> <div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center">
<div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center"> <tb-relation-type-autocomplete
<tb-relation-type-autocomplete class="cell" style="width: 200px; min-width: 200px;"
class="cell" style="width: 200px; min-width: 200px;" [formControl]="relationFilterControl.get('relationType')">
[formControl]="relationFilterControl.get('relationType')"> </tb-relation-type-autocomplete>
</tb-relation-type-autocomplete> <tb-entity-type-list class="cell" fxFlex
<tb-entity-type-list class="cell" fxFlex [allowedEntityTypes]="allowedEntityTypes"
[allowedEntityTypes]="allowedEntityTypes" [formControl]="relationFilterControl.get('entityTypes')">
[formControl]="relationFilterControl.get('entityTypes')"> </tb-entity-type-list>
</tb-entity-type-list> <button mat-icon-button color="primary"
<button mat-button mat-icon-button color="primary" type="button"
type="button" style="width: 40px; min-width: 40px;"
style="width: 40px; min-width: 40px;" (click)="removeFilter($index)"
(click)="removeFilter($index)" [disabled]="isLoading$ | async"
[disabled]="isLoading$ | async" matTooltip="{{ 'relation.remove-relation-filter' | translate }}"
matTooltip="{{ 'relation.remove-relation-filter' | translate }}" matTooltipPosition="above">
matTooltipPosition="above"> <mat-icon>close</mat-icon>
<mat-icon>close</mat-icon> </button>
</button> </div>
</div> </div>
</div> </div>
</div> </div>
@ -53,7 +55,7 @@
class="tb-prompt" translate>relation.any-relation</span> class="tb-prompt" translate>relation.any-relation</span>
</div> </div>
<div> <div>
<button mat-button mat-raised-button color="primary" <button mat-raised-button color="primary"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="addFilter()" (click)="addFilter()"
type="button" type="button"

24
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss

@ -15,10 +15,17 @@
*/ */
:host { :host {
.tb-relation-filters { .tb-relation-filters {
max-width: calc(100vw - 48px);
overflow: hidden;
.container{
width: 100%;
overflow: auto;
}
.header { .header {
padding-right: 5px; width: max-content;
padding-bottom: 5px; padding: 0 5px 5px;
padding-left: 5px;
.cell { .cell {
padding-right: 5px; padding-right: 5px;
@ -31,11 +38,8 @@
} }
.body { .body {
max-height: 300px; width: max-content;
padding-right: 5px; padding: 0 5px 20px;
padding-bottom: 20px;
padding-left: 5px;
overflow: auto;
.row { .row {
padding-top: 5px; padding-top: 5px;
@ -46,6 +50,10 @@
padding-left: 5px; padding-left: 5px;
} }
} }
.any-filter{
margin: 10px 0 20px;
}
} }
} }

16
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts

@ -14,21 +14,17 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, FormArray, ControlValueAccessor,
FormBuilder, FormControl, FormArray,
FormBuilder,
FormGroup, FormGroup,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR
ValidatorFn,
Validators
} from '@angular/forms'; } from '@angular/forms';
import { AliasFilterType, aliasFilterTypeTranslationMap, EntityAliasFilter } from '@shared/models/alias.models';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
import { TranslateService } from '@ngx-translate/core'; import { EntityTypeFilter } from '@shared/models/relation.models';
import { EntityService } from '@core/http/entity.service';
import { EntitySearchDirection, entitySearchDirectionTranslations, EntityTypeFilter } from '@shared/models/relation.models';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';

4
ui-ngx/src/app/modules/home/components/relation/relation-table.component.html

@ -86,7 +86,7 @@
</div> </div>
</mat-toolbar> </mat-toolbar>
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<mat-table [dataSource]="dataSource" <table mat-table [dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="select" sticky> <ng-container matColumnDef="select" sticky>
<mat-header-cell *matHeaderCellDef style="width: 30px;"> <mat-header-cell *matHeaderCellDef style="width: 30px;">
@ -156,7 +156,7 @@
<mat-row [ngClass]="{'mat-row-select': true, <mat-row [ngClass]="{'mat-row-select': true,
'mat-selected': dataSource.selection.isSelected(relation)}" 'mat-selected': dataSource.selection.isSelected(relation)}"
*matRowDef="let relation; columns: displayedColumns;" (click)="dataSource.selection.toggle(relation)"></mat-row> *matRowDef="let relation; columns: displayedColumns;" (click)="dataSource.selection.toggle(relation)"></mat-row>
</mat-table> </table>
<span [fxShow]="dataSource.isEmpty() | async" <span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>{{ 'relation.no-relations-text' }}</span> class="no-data-found" translate>{{ 'relation.no-relations-text' }}</span>

2
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html

@ -17,7 +17,7 @@
--> -->
<div class="tb-custom-action-pretty mat-elevation-z1" tb-fullscreen [fullscreen]="fullscreen"> <div class="tb-custom-action-pretty mat-elevation-z1" tb-fullscreen [fullscreen]="fullscreen">
<div fxLayout="row" fxLayoutAlign="end center" class="tb-action-expand-button" [ngClass]="{'tb-fullscreen-editor': fullscreen}"> <div fxLayout="row" fxLayoutAlign="end center" class="tb-action-expand-button" [ngClass]="{'tb-fullscreen-editor': fullscreen}">
<button mat-button fxHide.xs fxHide.sm <button mat-button fxHide.lt-md
matTooltip="{{ 'widget.toggle-fullscreen' | translate }}" matTooltip="{{ 'widget.toggle-fullscreen' | translate }}"
matTooltipPosition="above" matTooltipPosition="above"
(click)="fullscreen = !fullscreen"> (click)="fullscreen = !fullscreen">

34
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts

@ -19,46 +19,22 @@
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy,
Component, Component,
ElementRef, ElementRef,
forwardRef, forwardRef,
Input, Input,
OnDestroy, OnDestroy,
OnInit, OnInit,
ViewChild, ViewEncapsulation, ViewChildren, QueryList, ComponentFactoryResolver QueryList,
ViewChildren,
ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { MatDialog } from '@angular/material/dialog'; import { combineLatest } from 'rxjs';
import { DialogService } from '@core/services/dialog.service'; import { CustomActionDescriptor } from '@shared/models/widget.models';
import { PageLink } from '@shared/models/page/page-link';
import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { combineLatest, fromEvent, merge } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import {
WidgetActionDescriptorInfo,
WidgetActionsData,
WidgetActionsDatasource,
WidgetActionCallbacks, toWidgetActionDescriptor
} from '@home/components/widget/action/manage-widget-actions.component.models';
import { UtilsService } from '@core/services/utils.service';
import { EntityRelation, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models';
import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component';
import { CustomActionDescriptor, WidgetActionDescriptor, WidgetActionSource } from '@shared/models/widget.models';
import {
WidgetActionDialogComponent,
WidgetActionDialogData
} from '@home/components/widget/action/widget-action-dialog.component';
import { deepClone } from '@core/utils';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { CustomActionPrettyResourcesTabsComponent } from '@home/components/widget/action/custom-action-pretty-resources-tabs.component';
import { MatTab, MatTabGroup } from '@angular/material/tabs';
@Component({ @Component({
selector: 'tb-custom-action-pretty-editor', selector: 'tb-custom-action-pretty-editor',

4
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html

@ -23,12 +23,12 @@
fxLayoutAlign="start center" fxLayoutAlign="start center"
*ngFor="let resource of action.customResources; let i = index" > *ngFor="let resource of action.customResources; let i = index" >
<mat-form-field fxFlex class="mat-block resource-field" floatLabel="never" <mat-form-field fxFlex class="mat-block resource-field" floatLabel="never"
style="margin: 10px 0px 0px 0px; max-height: 40px;"> style="margin: 10px 0 0; max-height: 40px;">
<input required matInput [(ngModel)]="resource.url" <input required matInput [(ngModel)]="resource.url"
(ngModelChange)="notifyActionUpdated()" (ngModelChange)="notifyActionUpdated()"
placeholder="{{ 'widget.resource-url' | translate }}"/> placeholder="{{ 'widget.resource-url' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-button mat-icon-button color="primary" <button mat-icon-button color="primary"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
type="button" type="button"
(click)="removeResource(i)" (click)="removeResource(i)"

7
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts

@ -22,9 +22,10 @@ import {
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
Output, QueryList, Output,
SimpleChanges, SimpleChanges,
ViewChild, ViewChildren, ViewEncapsulation ViewChild,
ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -34,8 +35,6 @@ import { CustomActionDescriptor } from '@shared/models/widget.models';
import * as ace from 'ace-builds'; import * as ace from 'ace-builds';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { css_beautify, html_beautify } from 'js-beautify'; import { css_beautify, html_beautify } from 'js-beautify';
import { MatTab } from '@angular/material/tabs';
import { BehaviorSubject } from 'rxjs';
@Component({ @Component({
selector: 'tb-custom-action-pretty-resources-tabs', selector: 'tb-custom-action-pretty-resources-tabs',

16
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html

@ -21,14 +21,14 @@
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<span class="tb-entity-table-title" translate>widget-config.actions</span> <span class="tb-entity-table-title" translate>widget-config.actions</span>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
(click)="addAction($event)" (click)="addAction($event)"
matTooltip="{{ 'widget-config.add-action' | translate }}" matTooltip="{{ 'widget-config.add-action' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>
<button mat-button mat-icon-button [disabled]="isLoading$ | async" (click)="enterFilterMode()" <button mat-icon-button [disabled]="isLoading$ | async" (click)="enterFilterMode()"
type="button" type="button"
matTooltip="{{ 'action.search' | translate }}" matTooltip="{{ 'action.search' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -38,7 +38,7 @@
</mat-toolbar> </mat-toolbar>
<mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode"> <mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<button mat-button mat-icon-button <button mat-icon-button
type="button" type="button"
matTooltip="{{ 'widget-config.search-actions' | translate }}" matTooltip="{{ 'widget-config.search-actions' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -50,7 +50,7 @@
[(ngModel)]="pageLink.textSearch" [(ngModel)]="pageLink.textSearch"
placeholder="{{ 'widget-config.search-actions' | translate }}"/> placeholder="{{ 'widget-config.search-actions' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"
type="button" type="button"
matTooltip="{{ 'action.close' | translate }}" matTooltip="{{ 'action.close' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -59,7 +59,7 @@
</div> </div>
</mat-toolbar> </mat-toolbar>
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<mat-table [dataSource]="dataSource" <table mat-table [dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="actionSourceName"> <ng-container matColumnDef="actionSourceName">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 20%"> {{ 'widget-config.action-source' | translate }} </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="width: 20%"> {{ 'widget-config.action-source' | translate }} </mat-header-cell>
@ -90,14 +90,14 @@
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let action"> <mat-cell *matCellDef="let action">
<div fxFlex fxLayout="row" fxLayoutAlign="end"> <div fxFlex fxLayout="row" fxLayoutAlign="end">
<button mat-button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
matTooltip="{{ 'widget-config.edit-action' | translate }}" matTooltip="{{ 'widget-config.edit-action' | translate }}"
matTooltipPosition="above" matTooltipPosition="above"
(click)="editAction($event, action)"> (click)="editAction($event, action)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button mat-button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
matTooltip="{{ 'widget-config.delete-action' | translate }}" matTooltip="{{ 'widget-config.delete-action' | translate }}"
matTooltipPosition="above" matTooltipPosition="above"
@ -110,7 +110,7 @@
<mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'mat-row-select': true}" <mat-row [ngClass]="{'mat-row-select': true}"
*matRowDef="let action; columns: displayedColumns;"></mat-row> *matRowDef="let action; columns: displayedColumns;"></mat-row>
</mat-table> </table>
<span [fxShow]="dataSource.isEmpty() | async" <span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>{{ 'widget-config.no-actions-text' }}</span> class="no-data-found" translate>{{ 'widget-config.no-actions-text' }}</span>

15
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.models.ts

@ -14,19 +14,18 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { WidgetActionDescriptor, WidgetActionSource, import {
widgetActionTypeTranslationMap, CustomActionDescriptor } from '@app/shared/models/widget.models'; CustomActionDescriptor,
WidgetActionDescriptor,
WidgetActionSource,
widgetActionTypeTranslationMap
} from '@app/shared/models/widget.models';
import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { EntityRelationInfo, EntitySearchDirection } from '@shared/models/relation.models';
import { BehaviorSubject, Observable, of, ReplaySubject } from 'rxjs'; import { BehaviorSubject, Observable, of, ReplaySubject } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { SelectionModel } from '@angular/cdk/collections';
import { EntityRelationService } from '@core/http/entity-relation.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityId } from '@shared/models/id/entity-id';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { catchError, map, publishReplay, refCount, share, take, tap } from 'rxjs/operators'; import { catchError, map, publishReplay, refCount } from 'rxjs/operators';
import { entityTypeTranslations } from '@shared/models/entity-type.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { deepClone, isDefined, isUndefined } from '@core/utils'; import { deepClone, isDefined, isUndefined } from '@core/utils';

19
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts

@ -14,17 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
AfterViewInit,
ChangeDetectionStrategy,
Component,
ElementRef,
forwardRef,
Input,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -39,14 +29,13 @@ import { MatSort } from '@angular/material/sort';
import { fromEvent, merge } from 'rxjs'; import { fromEvent, merge } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { import {
toWidgetActionDescriptor,
WidgetActionCallbacks,
WidgetActionDescriptorInfo, WidgetActionDescriptorInfo,
WidgetActionsData, WidgetActionsData,
WidgetActionsDatasource, WidgetActionsDatasource
WidgetActionCallbacks, toWidgetActionDescriptor
} from '@home/components/widget/action/manage-widget-actions.component.models'; } from '@home/components/widget/action/manage-widget-actions.component.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { EntityRelation, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models';
import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component';
import { WidgetActionDescriptor, WidgetActionSource } from '@shared/models/widget.models'; import { WidgetActionDescriptor, WidgetActionSource } from '@shared/models/widget.models';
import { import {
WidgetActionDialogComponent, WidgetActionDialogComponent,

20
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<form #widgetActionForm="ngForm" [formGroup]="widgetActionFormGroup" (ngSubmit)="save()" style="min-width: 600px;"> <form #widgetActionForm="ngForm" [formGroup]="widgetActionFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2>{{ (isAdd ? 'widget-config.add-action' : 'widget-config.edit-action' ) | translate }}</h2> <h2>{{ (isAdd ? 'widget-config.add-action' : 'widget-config.edit-action' ) | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -32,7 +32,7 @@
<fieldset [disabled]="isLoading$ | async"> <fieldset [disabled]="isLoading$ | async">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>widget-config.action-source</mat-label> <mat-label translate>widget-config.action-source</mat-label>
<mat-select required matInput formControlName="actionSourceId"> <mat-select required formControlName="actionSourceId">
<mat-option *ngFor="let actionSourceItem of data.actionsData.actionSources | keyvalue" [value]="actionSourceItem.key"> <mat-option *ngFor="let actionSourceItem of data.actionsData.actionSources | keyvalue" [value]="actionSourceItem.key">
{{ actionSourceName(actionSourceItem.value) }} {{ actionSourceName(actionSourceItem.value) }}
</mat-option> </mat-option>
@ -41,7 +41,7 @@
{{ 'widget-config.action-source-required' | translate }} {{ 'widget-config.action-source-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block" style="padding-bottom: 20px;"> <mat-form-field class="mat-block">
<mat-label translate>widget-config.action-name</mat-label> <mat-label translate>widget-config.action-name</mat-label>
<input required matInput formControlName="name"> <input required matInput formControlName="name">
<mat-error *ngIf="widgetActionFormGroup.get('name').hasError('required')"> <mat-error *ngIf="widgetActionFormGroup.get('name').hasError('required')">
@ -56,7 +56,7 @@
</tb-material-icon-select> </tb-material-icon-select>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>widget-config.action-type</mat-label> <mat-label translate>widget-config.action-type</mat-label>
<mat-select required matInput formControlName="type"> <mat-select required formControlName="type">
<mat-option *ngFor="let actionType of widgetActionTypes" [value]="actionType"> <mat-option *ngFor="let actionType of widgetActionTypes" [value]="actionType">
{{ widgetActionTypeTranslations.get(widgetActionType[actionType]) | translate }} {{ widgetActionTypeTranslations.get(widgetActionType[actionType]) | translate }}
</mat-option> </mat-option>
@ -67,7 +67,7 @@
</mat-form-field> </mat-form-field>
<section fxLayout="column" [formGroup]="actionTypeFormGroup" [ngSwitch]="widgetActionFormGroup.get('type').value"> <section fxLayout="column" [formGroup]="actionTypeFormGroup" [ngSwitch]="widgetActionFormGroup.get('type').value">
<ng-template [ngSwitchCase]="widgetActionType.openDashboard"> <ng-template [ngSwitchCase]="widgetActionType.openDashboard">
<div fxLayout="column" style="padding-bottom: 20px;"> <div fxLayout="column">
<div class="mat-caption tb-required" <div class="mat-caption tb-required"
style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>widget-action.target-dashboard</div> style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>widget-action.target-dashboard</div>
<tb-dashboard-autocomplete <tb-dashboard-autocomplete
@ -89,7 +89,7 @@
[matAutocomplete]="targetDashboardStateAutocomplete"> [matAutocomplete]="targetDashboardStateAutocomplete">
<button *ngIf="actionTypeFormGroup.get('targetDashboardStateId').value" <button *ngIf="actionTypeFormGroup.get('targetDashboardStateId').value"
type="button" type="button"
matSuffix mat-button mat-icon-button aria-label="Clear" matSuffix mat-icon-button aria-label="Clear"
(click)="clearTargetDashboardState()"> (click)="clearTargetDashboardState()">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>
@ -145,15 +145,13 @@
</section> </section>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)"> [disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)">
{{ (isAdd ? 'action.add' : 'action.save') | translate }} {{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

20
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.scss

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 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.
*/
:host ::ng-deep {
.mat-checkbox-label {
white-space: normal;
}
}

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

@ -55,7 +55,7 @@ export interface WidgetActionDialogData {
selector: 'tb-widget-action-dialog', selector: 'tb-widget-action-dialog',
templateUrl: './widget-action-dialog.component.html', templateUrl: './widget-action-dialog.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: WidgetActionDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: WidgetActionDialogComponent}],
styleUrls: [] styleUrls: ['./widget-action-dialog.component.scss']
}) })
export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDialogComponent, export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDialogComponent,
WidgetActionDescriptorInfo> implements OnInit, ErrorStateMatcher { WidgetActionDescriptorInfo> implements OnInit, ErrorStateMatcher {

10
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<form #dataKeyForm="ngForm" [formGroup]="dataKeyFormGroup" (ngSubmit)="save()" style="width: 600px;"> <form #dataKeyForm="ngForm" [formGroup]="dataKeyFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2>{{ 'datakey.configuration' | translate }}</h2> <h2>{{ 'datakey.configuration' | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -36,15 +36,13 @@
formControlName="dataKey"> formControlName="dataKey">
</tb-data-key-config> </tb-data-key-config>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || dataKeyForm.invalid || !dataKeyForm.dirty"> [disabled]="(isLoading$ | async) || dataKeyForm.invalid || !dataKeyForm.dirty">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

1
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.scss

@ -17,5 +17,6 @@
.mat-dialog-content { .mat-dialog-content {
padding: 0 !important; padding: 0 !important;
margin: 0 !important; margin: 0 !important;
overflow: hidden;
} }
} }

21
ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html

@ -15,10 +15,9 @@
limitations under the License. limitations under the License.
--> -->
<mat-tab-group class="tb-datakey-config" [ngClass]="{'tb-headless': !displayAdvanced}" <mat-tab-group class="tb-datakey-config" [ngClass]="{'tb-headless': !displayAdvanced}">
class="tb-datakey-config">
<mat-tab [formGroup]="dataKeyFormGroup" label="{{ 'datakey.settings' | translate }}"> <mat-tab [formGroup]="dataKeyFormGroup" label="{{ 'datakey.settings' | translate }}">
<div class="mat-content mat-padding" fxLayout="column"> <div class="mat-padding settings" fxLayout="column">
<mat-form-field class="mat-block" *ngIf="modelValue.type !== dataKeyTypes.function"> <mat-form-field class="mat-block" *ngIf="modelValue.type !== dataKeyTypes.function">
<mat-label>{{ 'entity.key' | translate }}</mat-label> <mat-label>{{ 'entity.key' | translate }}</mat-label>
<input matInput type="text" placeholder="{{ 'entity.key-name' | translate }}" <input matInput type="text" placeholder="{{ 'entity.key-name' | translate }}"
@ -28,7 +27,7 @@
[matAutocomplete]="keyAutocomplete"> [matAutocomplete]="keyAutocomplete">
<button *ngIf="dataKeyFormGroup.get('name').value" <button *ngIf="dataKeyFormGroup.get('name').value"
type="button" type="button"
matSuffix mat-button mat-icon-button aria-label="Clear" matSuffix mat-icon-button aria-label="Clear"
(click)="clearKey()"> (click)="clearKey()">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>
@ -40,7 +39,7 @@
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="start center" fxLayoutAlign.xs fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>datakey.label</mat-label> <mat-label translate>datakey.label</mat-label>
<input matInput formControlName="label" required> <input matInput formControlName="label" required>
@ -53,7 +52,7 @@
formControlName="color"> formControlName="color">
</tb-color-input> </tb-color-input>
</div> </div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" *ngIf="modelValue.type !== dataKeyTypes.alarm"> <div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="start center" fxLayoutAlign.xs fxLayoutGap="8px" *ngIf="modelValue.type !== dataKeyTypes.alarm">
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label translate>datakey.units</mat-label> <mat-label translate>datakey.units</mat-label>
<input matInput formControlName="units"> <input matInput formControlName="units">
@ -94,12 +93,10 @@
</div> </div>
</mat-tab> </mat-tab>
<mat-tab [formGroup]="dataKeySettingsFormGroup" label="{{ 'datakey.advanced' | translate }}" *ngIf="displayAdvanced"> <mat-tab [formGroup]="dataKeySettingsFormGroup" label="{{ 'datakey.advanced' | translate }}" *ngIf="displayAdvanced">
<div class="mat-content mat-padding" fxLayout="column"> <div class="mat-padding" fxLayout="column">
<div style="overflow: auto;"> <tb-json-form
<tb-json-form formControlName="settings">
formControlName="settings"> </tb-json-form>
</tb-json-form>
</div>
</div> </div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

22
ui-ngx/src/app/modules/home/components/widget/data-key-config.component.scss

@ -13,11 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
@import '../../../../../scss/constants';
:host { :host {
.tb-datakey-config { .tb-datakey-config {
min-width: 500px !important; height: 100%;
min-height: 500px !important;
height: 500px !important; .settings{
&> :first-child{
padding-top: 12px;
}
&> :nth-child(n){
padding-right: 12px;
padding-left: 12px;
}
}
} }
} }
@ -25,7 +35,11 @@
.tb-datakey-config { .tb-datakey-config {
.mat-tab-body.mat-tab-body-active { .mat-tab-body.mat-tab-body-active {
.mat-tab-body-content > div { .mat-tab-body-content > div {
height: 100%; height: calc(65vh - 50px);
overflow: initial;
@media #{$mat-xs} {
height: 500px;
}
} }
} }
} }

5
ui-ngx/src/app/modules/home/components/widget/data-keys.component.scss

@ -15,6 +15,8 @@
*/ */
:host { :host {
.mat-chip.mat-standard-chip { .mat-chip.mat-standard-chip {
overflow: hidden;
.tb-attribute-chip { .tb-attribute-chip {
max-width: 100%; max-width: 100%;
color: rgb(66, 66, 66); color: rgb(66, 66, 66);
@ -32,7 +34,8 @@
.tb-chip-labels { .tb-chip-labels {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
min-width: 0px; align-items: flex-end;
min-width: 0;
.tb-chip-label { .tb-chip-label {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

14
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts

@ -16,23 +16,21 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { import {
Inject, Component,
ComponentRef,
ElementRef,
ViewContainerRef,
ComponentFactory, ComponentFactory,
ComponentRef,
Inject,
Injector, Injector,
ReflectiveInjector, OnDestroy,
Component, ViewContainerRef
OnDestroy
} from '@angular/core'; } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { import {
CustomDialogComponent,
CUSTOM_DIALOG_DATA, CUSTOM_DIALOG_DATA,
CustomDialogComponent,
CustomDialogData CustomDialogData
} from '@home/components/widget/dialog/custom-dialog.component'; } from '@home/components/widget/dialog/custom-dialog.component';

3
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts

@ -14,9 +14,8 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { Inject, InjectionToken } from '@angular/core'; import { Inject, InjectionToken } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Router } from '@angular/router'; import { Router } from '@angular/router';

1
ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts

@ -21,7 +21,6 @@ import { AppState } from '@core/core.state';
import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models'; import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service'; import { RafService } from '@core/services/raf.service';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { import {
NotificationHorizontalPosition, NotificationHorizontalPosition,
NotificationType, NotificationType,

4
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html

@ -61,7 +61,7 @@
</div> </div>
</mat-toolbar> </mat-toolbar>
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<mat-table [dataSource]="alarmsDatasource" <table mat-table [dataSource]="alarmsDatasource"
matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="select" sticky> <ng-container matColumnDef="select" sticky>
<mat-header-cell *matHeaderCellDef style="width: 30px;"> <mat-header-cell *matHeaderCellDef style="width: 30px;">
@ -124,7 +124,7 @@
'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}" 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}"
*matRowDef="let alarm; columns: displayedColumns;" *matRowDef="let alarm; columns: displayedColumns;"
(click)="onRowClick($event, alarm)"></mat-row> (click)="onRowClick($event, alarm)"></mat-row>
</mat-table> </table>
<span [fxShow]="alarmsDatasource.isEmpty() | async" <span [fxShow]="alarmsDatasource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>alarm.no-alarms-prompt</span> class="no-data-found" translate>alarm.no-alarms-prompt</span>

4
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -38,7 +38,7 @@
</div> </div>
</mat-toolbar> </mat-toolbar>
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<mat-table [dataSource]="entityDatasource" <table mat-table [dataSource]="entityDatasource"
matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container [matColumnDef]="column.def" *ngFor="let column of columns; trackBy: trackByColumnDef;"> <ng-container [matColumnDef]="column.def" *ngFor="let column of columns; trackBy: trackByColumnDef;">
<mat-header-cell [ngStyle]="headerStyle(column)" *matHeaderCellDef mat-sort-header> {{ column.title }} </mat-header-cell> <mat-header-cell [ngStyle]="headerStyle(column)" *matHeaderCellDef mat-sort-header> {{ column.title }} </mat-header-cell>
@ -85,7 +85,7 @@
<mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}" <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}"
*matRowDef="let entity; columns: displayedColumns;" *matRowDef="let entity; columns: displayedColumns;"
(click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</mat-table> </table>
<span [fxShow]="entityDatasource.isEmpty() | async" <span [fxShow]="entityDatasource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>entity.no-entities-prompt</span> class="no-data-found" translate>entity.no-entities-prompt</span>

4
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html

@ -41,7 +41,7 @@
[(selectedIndex)]="sourceIndex" (selectedIndexChange)="onSourceIndexChanged()"> [(selectedIndex)]="sourceIndex" (selectedIndexChange)="onSourceIndexChanged()">
<mat-tab *ngFor="let source of sources" label="{{ source.datasource.name }}"> <mat-tab *ngFor="let source of sources" label="{{ source.datasource.name }}">
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<mat-table [dataSource]="source.timeseriesDatasource" [trackBy]="trackByRowIndex" <table mat-table [dataSource]="source.timeseriesDatasource" [trackBy]="trackByRowIndex"
matSort [matSortActive]="source.pageLink.sortOrder.property" [matSortDirection]="source.pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="source.pageLink.sortOrder.property" [matSortDirection]="source.pageLink.sortDirection()" matSortDisableClear>
<ng-container *ngIf="showTimestamp" [matColumnDef]="'0'"> <ng-container *ngIf="showTimestamp" [matColumnDef]="'0'">
<mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell>
@ -94,7 +94,7 @@
<mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: true"></mat-header-row> <mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: true"></mat-header-row>
<mat-row *matRowDef="let row; columns: source.displayedColumns;" <mat-row *matRowDef="let row; columns: source.displayedColumns;"
(click)="onRowClick($event, row)"></mat-row> (click)="onRowClick($event, row)"></mat-row>
</mat-table> </table>
<span [fxShow]="source.timeseriesDatasource.isEmpty() | async" <span [fxShow]="source.timeseriesDatasource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>widget.no-data-found</span> class="no-data-found" translate>widget.no-data-found</span>

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

@ -16,12 +16,12 @@
--> -->
<mat-tab-group [ngClass]="{'tb-headless': (widgetType === widgetTypes.static && !displayAdvanced())}" <mat-tab-group [ngClass]="{'tb-headless': (widgetType === widgetTypes.static && !displayAdvanced())}"
fxFlex 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 }}" [fxShow]="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" <div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm" fxFlex="100"
fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center"> fxLayout.xs="column" fxLayoutGap="8px" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center">
<div fxLayout="column" fxLayoutGap="8px" fxFlex> <div fxLayout="column" fxLayoutGap="8px" fxFlex.gt-xs>
<mat-checkbox formControlName="useDashboardTimewindow"> <mat-checkbox formControlName="useDashboardTimewindow">
{{ 'widget-config.use-dashboard-timewindow' | translate }} {{ 'widget-config.use-dashboard-timewindow' | translate }}
</mat-checkbox> </mat-checkbox>
@ -29,8 +29,10 @@
{{ 'widget-config.display-timewindow' | translate }} {{ 'widget-config.display-timewindow' | translate }}
</mat-checkbox> </mat-checkbox>
</div> </div>
<section fxFlex fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 16px;"> <section fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-lg="column" fxLayoutAlign.lt-lg="center start"
<span [ngClass]="{'tb-disabled-label': dataSettings.get('useDashboardTimewindow').value}" translate style="padding-right: 8px;">widget-config.timewindow</span> fxFlex.gt-xs style="margin-bottom: 16px;">
<span [ngClass]="{'tb-disabled-label': dataSettings.get('useDashboardTimewindow').value}" translate
style="padding-right: 8px;">widget-config.timewindow</span>
<tb-timewindow asButton="true" <tb-timewindow asButton="true"
isEdit="true" isEdit="true"
aggregation="{{ widgetType === widgetTypes.timeseries }}" aggregation="{{ widgetType === widgetTypes.timeseries }}"
@ -38,10 +40,11 @@
</section> </section>
</div> </div>
<div *ngIf="widgetType === widgetTypes.alarm" fxLayout="column" fxLayoutAlign="center"> <div *ngIf="widgetType === widgetTypes.alarm" fxLayout="column" fxLayoutAlign="center">
<div fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px"> <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center"
fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.alarm-status</mat-label> <mat-label translate>alarm.alarm-status</mat-label>
<mat-select matInput formControlName="alarmSearchStatus"> <mat-select formControlName="alarmSearchStatus">
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus"> <mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }} {{ ('alarm.search-status.' + searchStatus) | translate }}
</mat-option> </mat-option>
@ -61,7 +64,8 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px"> <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center"
fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.max-count-load</mat-label> <mat-label translate>alarm.max-count-load</mat-label>
<input matInput required <input matInput required
@ -100,7 +104,7 @@
<mat-panel-title fxLayout="column"> <mat-panel-title fxLayout="column">
<div class="tb-panel-title" translate>widget-config.datasources</div> <div class="tb-panel-title" translate>widget-config.datasources</div>
<div *ngIf="modelValue?.typeParameters && modelValue?.typeParameters.maxDatasources > -1" <div *ngIf="modelValue?.typeParameters && modelValue?.typeParameters.maxDatasources > -1"
class="tb-hint">{{ 'widget-config.maximum-datasources' | translate:{count: modelValue?.typeParameters.maxDatasources} }}</div> class="tb-panel-hint">{{ 'widget-config.maximum-datasources' | translate:{count: modelValue?.typeParameters.maxDatasources} }}</div>
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<div *ngIf="datasourcesFormArray().length === 0; else datasourcesTemplate"> <div *ngIf="datasourcesFormArray().length === 0; else datasourcesTemplate">
@ -133,7 +137,7 @@
fxLayout.gt-sm="row" fxLayout.gt-sm="row"
fxLayoutAlign.gt-sm="start center"> fxLayoutAlign.gt-sm="start center">
<mat-form-field class="tb-datasource-type"> <mat-form-field class="tb-datasource-type">
<mat-select matInput formControlName="type"> <mat-select formControlName="type">
<mat-option *ngFor="let datasourceType of datasourceTypes" [value]="datasourceType"> <mat-option *ngFor="let datasourceType of datasourceTypes" [value]="datasourceType">
{{ datasourceTypesTranslations.get(datasourceType) | translate }} {{ datasourceTypesTranslations.get(datasourceType) | translate }}
</mat-option> </mat-option>
@ -172,7 +176,7 @@
</section> </section>
<button [disabled]="isLoading$ | async" <button [disabled]="isLoading$ | async"
type="button" type="button"
mat-button mat-icon-button color="primary" mat-icon-button color="primary"
style="min-width: 40px;" style="min-width: 40px;"
(click)="removeDatasource($index)" (click)="removeDatasource($index)"
matTooltip="{{ 'widget-config.remove-datasource' | translate }}" matTooltip="{{ 'widget-config.remove-datasource' | translate }}"
@ -186,7 +190,7 @@
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> <div fxFlex fxLayout="row" fxLayoutAlign="start center">
<button [disabled]="isLoading$ | async" <button [disabled]="isLoading$ | async"
type="button" type="button"
mat-button mat-raised-button color="primary" mat-raised-button color="primary"
[fxShow]="modelValue?.typeParameters && [fxShow]="modelValue?.typeParameters &&
(modelValue?.typeParameters.maxDatasources == -1 || datasourcesFormArray().controls.length < modelValue?.typeParameters.maxDatasources)" (modelValue?.typeParameters.maxDatasources == -1 || datasourcesFormArray().controls.length < modelValue?.typeParameters.maxDatasources)"
(click)="addDatasource()" (click)="addDatasource()"
@ -228,7 +232,7 @@
fxLayout.gt-sm="row" fxLayout.gt-sm="row"
fxLayoutAlign.gt-sm="start center"> fxLayoutAlign.gt-sm="start center">
<mat-form-field class="tb-datasource-type"> <mat-form-field class="tb-datasource-type">
<mat-select matInput formControlName="type"> <mat-select formControlName="type">
<mat-option *ngFor="let datasourceType of datasourceTypes" [value]="datasourceType"> <mat-option *ngFor="let datasourceType of datasourceTypes" [value]="datasourceType">
{{ datasourceTypesTranslations.get(datasourceType) | translate }} {{ datasourceTypesTranslations.get(datasourceType) | translate }}
</mat-option> </mat-option>
@ -268,15 +272,20 @@
</div> </div>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'widget-config.settings' | translate }}"> <mat-tab label="{{ 'widget-config.settings' | translate }}">
<div class="mat-content mat-padding" <div class="mat-content mat-padding" fxLayout="column" fxLayoutGap="8px">
fxLayout="column" fxLayoutGap="8px">
<div [formGroup]="widgetSettings" fxLayout="column" fxLayoutGap="8px"> <div [formGroup]="widgetSettings" fxLayout="column" fxLayoutGap="8px">
<span translate>widget-config.general-settings</span> <span translate>widget-config.general-settings</span>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center"> <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center">
<mat-form-field fxFlex class="mat-block"> <div fxLayout="column" fxLayoutAlign="center" fxFlex.sm="40%" fxFlex.gt-sm="30%">
<mat-label translate>widget-config.title</mat-label> <mat-form-field fxFlex class="mat-block">
<input matInput formControlName="title"> <mat-label translate>widget-config.title</mat-label>
</mat-form-field> <input matInput formControlName="title">
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>widget-config.title-tooltip</mat-label>
<input matInput formControlName="titleTooltip">
</mat-form-field>
</div>
<div fxFlex [fxShow]="widgetSettings.get('showTitle').value"> <div fxFlex [fxShow]="widgetSettings.get('showTitle').value">
<tb-json-object-edit <tb-json-object-edit
[editorStyle]="{minHeight: '100px'}" [editorStyle]="{minHeight: '100px'}"
@ -286,46 +295,40 @@
></tb-json-object-edit> ></tb-json-object-edit>
</div> </div>
</div> </div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-md="row" fxLayoutAlign.gt-md="start center" fxFlex="100%"
fxLayoutGap="8px"> fxLayoutGap="8px">
<div fxLayout="row" fxFlex> <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
<mat-checkbox formControlName="showTitleIcon"> fxLayoutGap="8px" fxFlex.gt-md>
<mat-checkbox fxFlex formControlName="showTitleIcon">
{{ 'widget-config.display-icon' | translate }} {{ 'widget-config.display-icon' | translate }}
</mat-checkbox> </mat-checkbox>
</div> <tb-material-icon-select fxFlex
<div fxFlex>
<tb-material-icon-select
formControlName="titleIcon"> formControlName="titleIcon">
</tb-material-icon-select> </tb-material-icon-select>
</div> </div>
<tb-color-input fxFlex <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
label="{{'widget-config.icon-color' | translate}}" fxLayoutGap="8px" fxFlex.gt-md>
icon="format_color_fill" <tb-color-input fxFlex
openOnInput label="{{'widget-config.icon-color' | translate}}"
formControlName="iconColor"> icon="format_color_fill"
</tb-color-input> openOnInput
<mat-form-field fxFlex> formControlName="iconColor">
<mat-label translate>widget-config.icon-size</mat-label> </tb-color-input>
<input matInput formControlName="iconSize"> <mat-form-field fxFlex>
</mat-form-field> <mat-label translate>widget-config.icon-size</mat-label>
<mat-form-field fxFlex> <input matInput formControlName="iconSize">
<mat-label translate>widget-config.title-tooltip</mat-label> </mat-form-field>
<input matInput formControlName="titleTooltip"> </div>
</mat-form-field>
</div> </div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
fxLayoutGap="8px"> fxLayoutGap="8px">
<div fxLayout="row"> <div fxLayout="column" fxLayoutAlign="center" fxLayoutGap="8px" fxFlex.sm="40%" fxFlex.gt-sm="30%">
<mat-checkbox formControlName="showTitle"> <mat-checkbox formControlName="showTitle">
{{ 'widget-config.display-title' | translate }} {{ 'widget-config.display-title' | translate }}
</mat-checkbox> </mat-checkbox>
</div>
<div fxLayout="row">
<mat-checkbox formControlName="dropShadow"> <mat-checkbox formControlName="dropShadow">
{{ 'widget-config.drop-shadow' | translate }} {{ 'widget-config.drop-shadow' | translate }}
</mat-checkbox> </mat-checkbox>
</div>
<div fxLayout="row">
<mat-checkbox formControlName="enableFullscreen"> <mat-checkbox formControlName="enableFullscreen">
{{ 'widget-config.enable-fullscreen' | translate }} {{ 'widget-config.enable-fullscreen' | translate }}
</mat-checkbox> </mat-checkbox>
@ -339,30 +342,36 @@
></tb-json-object-edit> ></tb-json-object-edit>
</div> </div>
</div> </div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-md="row" fxLayoutAlign.gt-md="start center"
fxLayoutGap="8px"> fxFlex="100%" fxLayoutGap="8px">
<tb-color-input fxFlex <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
label="{{'widget-config.background-color' | translate}}" fxLayoutGap="8px" fxFlex.gt-md>
icon="format_color_fill" <tb-color-input fxFlex
openOnInput label="{{'widget-config.background-color' | translate}}"
formControlName="backgroundColor"> icon="format_color_fill"
</tb-color-input> openOnInput
<tb-color-input fxFlex formControlName="backgroundColor">
label="{{'widget-config.text-color' | translate}}" </tb-color-input>
icon="format_color_fill" <tb-color-input fxFlex
openOnInput label="{{'widget-config.text-color' | translate}}"
formControlName="color"> icon="format_color_fill"
</tb-color-input> openOnInput
<mat-form-field fxFlex> formControlName="color">
<mat-label translate>widget-config.padding</mat-label> </tb-color-input>
<input matInput formControlName="padding"> </div>
</mat-form-field> <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
<mat-form-field fxFlex> fxLayoutGap="8px" fxFlex.gt-md>
<mat-label translate>widget-config.margin</mat-label> <mat-form-field fxFlex>
<input matInput formControlName="margin"> <mat-label translate>widget-config.padding</mat-label>
</mat-form-field> <input matInput formControlName="padding">
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>widget-config.margin</mat-label>
<input matInput formControlName="margin">
</mat-form-field>
</div>
</div> </div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
fxLayoutGap="8px"> fxLayoutGap="8px">
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label translate>widget-config.units</mat-label> <mat-label translate>widget-config.units</mat-label>
@ -373,14 +382,13 @@
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1"> <input matInput formControlName="decimals" type="number" min="0" max="15" step="1">
</mat-form-field> </mat-form-field>
</div> </div>
<div [fxShow]="widgetType === widgetTypes.timeseries || <div [fxShow]="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.latest"
widgetType === widgetTypes.latest" fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px" fxFlex="100%">
fxLayoutGap="8px"> <mat-checkbox fxFlex.gt-xs formControlName="showLegend">
<mat-checkbox fxFlex formControlName="showLegend">
{{ 'widget-config.display-legend' | translate }} {{ 'widget-config.display-legend' | translate }}
</mat-checkbox> </mat-checkbox>
<section fxFlex fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 16px;"> <section fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 16px;">
<tb-legend-config formControlName="legendConfig"> <tb-legend-config formControlName="legendConfig">
</tb-legend-config> </tb-legend-config>
</section> </section>
@ -388,7 +396,7 @@
</div> </div>
<div [formGroup]="layoutSettings" fxLayout="column" fxLayoutGap="8px"> <div [formGroup]="layoutSettings" fxLayout="column" fxLayoutGap="8px">
<span translate>widget-config.mobile-mode-settings</span> <span translate>widget-config.mobile-mode-settings</span>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" <div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row" fxLayoutAlign="start center"
fxLayoutGap="8px"> fxLayoutGap="8px">
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label translate>widget-config.order</mat-label> <mat-label translate>widget-config.order</mat-label>

24
ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss

@ -57,14 +57,26 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
} }
.mat-tab-body.mat-tab-body-active { .tb-panel-hint {
.mat-tab-body-content > div { font-size: 12px;
height: 100%; color: #808080;
} }
//.mat-tab-body.mat-tab-body-active {
// .mat-tab-body-content > div {
// height: 100%;
// }
//}
.mat-checkbox-label {
white-space: normal;
} }
.mat-expansion-panel { .mat-expansion-panel {
&.tb-datasources.mat-expanded { &.tb-datasources{
overflow: visible; &.mat-expanded {
overflow: visible;
}
.mat-expansion-panel-body{
padding: 0 12px 16px;
}
} }
.mat-expansion-panel-content { .mat-expansion-panel-content {
font: inherit; font: inherit;

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

@ -50,7 +50,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
sidenavMode: 'over' | 'push' | 'side' = 'side'; sidenavMode: 'over' | 'push' | 'side' = 'side';
sidenavOpened = true; sidenavOpened = true;
logo = require('../../../assets/logo_title_white.svg'); logo = require('../../../assets/logo_title_white.svg').default;
@ViewChild('sidenav') @ViewChild('sidenav')
sidenav: MatSidenav; sidenav: MatSidenav;

13
ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.html

@ -16,11 +16,11 @@
--> -->
<form #widgetForm="ngForm" [formGroup]="widgetFormGroup" (ngSubmit)="add()" style="width: 900px;"> <form #widgetForm="ngForm" [formGroup]="widgetFormGroup" (ngSubmit)="add()" style="width: 900px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>widget.add</h2> <h2 translate>widget.add</h2>
<span fxFlex></span> <span fxFlex></span>
<div [tb-help]="helpLinkIdForWidgetType()"></div> <div [tb-help]="helpLinkIdForWidgetType()"></div>
<button mat-button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
@ -39,18 +39,17 @@
</tb-widget-config> </tb-widget-config>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || widgetFormGroup.invalid"> [disabled]="(isLoading$ | async) || widgetFormGroup.invalid">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()"
cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
</div> </div>

8
ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.ts

@ -23,9 +23,6 @@ import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { Widget, widgetTypesData } from '@shared/models/widget.models'; import { Widget, widgetTypesData } from '@shared/models/widget.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { EntityService } from '@core/http/entity.service';
import { Dashboard } from '@app/shared/models/dashboard.models'; import { Dashboard } from '@app/shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-component.models'; import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-component.models';
@ -60,10 +57,7 @@ export class AddWidgetDialogComponent extends DialogComponent<AddWidgetDialogCom
@Inject(MAT_DIALOG_DATA) public data: AddWidgetDialogData, @Inject(MAT_DIALOG_DATA) public data: AddWidgetDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<AddWidgetDialogComponent, Widget>, public dialogRef: MatDialogRef<AddWidgetDialogComponent, Widget>,
private fb: FormBuilder, private fb: FormBuilder) {
private utils: UtilsService,
private translate: TranslateService,
private entityService: EntityService) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.dashboard = this.data.dashboard; this.dashboard = this.data.dashboard;

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

@ -69,7 +69,7 @@
</mat-form-field> </mat-form-field>
<div fxLayout="column" [fxShow]="!isEdit && (dashboardScope === 'tenant' && isPublic(entity) <div fxLayout="column" [fxShow]="!isEdit && (dashboardScope === 'tenant' && isPublic(entity)
|| dashboardScope === 'customer' && isCurrentPublicCustomer(entity))"> || dashboardScope === 'customer' && isCurrentPublicCustomer(entity))">
<tb-social-share-panel style="padding-bottom: 10px;" <tb-social-share-panel style="padding-bottom: 1.25em;"
shareTitle="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: entity?.title} }}" shareTitle="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: entity?.title} }}"
shareText="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: entity?.title} }}" shareText="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: entity?.title} }}"
shareLink="{{ publicLink }}" shareLink="{{ publicLink }}"

24
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-page.component.html

@ -27,27 +27,27 @@
fxLayoutAlign="center stretch" fxLayoutAlign.gt-sm="space-between center"> fxLayoutAlign="center stretch" fxLayoutAlign.gt-sm="space-between center">
<div class="tb-dashboard-action-panel" fxFlex.md="30" fxLayout="row-reverse" <div class="tb-dashboard-action-panel" fxFlex.md="30" fxLayout="row-reverse"
fxLayoutAlign.gt-sm="start center" fxLayoutAlign="space-between center" fxLayoutGap="12px"> fxLayoutAlign.gt-sm="start center" fxLayoutAlign="space-between center" fxLayoutGap="12px">
<button [fxShow]="showCloseToolbar()" mat-button mat-icon-button class="close-action" <button [fxShow]="showCloseToolbar()" mat-icon-button class="close-action"
matTooltip="{{ 'dashboard.close-toolbar' | translate }}" matTooltip="{{ 'dashboard.close-toolbar' | translate }}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="closeToolbar()"> (click)="closeToolbar()">
<mat-icon>arrow_forward</mat-icon> <mat-icon>arrow_forward</mat-icon>
</button> </button>
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.xs fxHide.sm displayUserInfo="true"> <tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.lt-md displayUserInfo="true">
</tb-user-menu> </tb-user-menu>
<button [fxShow]="showRightLayoutSwitch()" mat-button mat-icon-button <button [fxShow]="showRightLayoutSwitch()" mat-icon-button
matTooltip="{{ (isRightLayoutOpened ? 'dashboard.hide-details' : 'dashboard.show-details') | translate }}" matTooltip="{{ (isRightLayoutOpened ? 'dashboard.hide-details' : 'dashboard.show-details') | translate }}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="toggleLayouts()"> (click)="toggleLayouts()">
<mat-icon>{{isRightLayoutOpened ? 'arrow_back' : 'menu'}}</mat-icon> <mat-icon>{{isRightLayoutOpened ? 'arrow_back' : 'menu'}}</mat-icon>
</button> </button>
<button [fxShow]="!hideFullscreenButton()" mat-button mat-icon-button <button [fxShow]="!hideFullscreenButton()" mat-icon-button
matTooltip="{{(isFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(isFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="isFullscreen = !isFullscreen"> (click)="isFullscreen = !isFullscreen">
<mat-icon>{{ isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> <mat-icon>{{ isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
</button> </button>
<button [fxShow]="isEdit || displayExport()" mat-button mat-icon-button <button [fxShow]="isEdit || displayExport()" mat-icon-button
matTooltip="{{'dashboard.export' | translate}}" matTooltip="{{'dashboard.export' | translate}}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="exportDashboard($event)"> (click)="exportDashboard($event)">
@ -65,13 +65,13 @@
tooltipPosition="below" tooltipPosition="below"
[aliasController]="dashboardCtx.aliasController"> [aliasController]="dashboardCtx.aliasController">
</tb-aliases-entity-select> </tb-aliases-entity-select>
<button [fxShow]="isEdit" mat-button mat-icon-button <button [fxShow]="isEdit" mat-icon-button
matTooltip="{{ 'entity.aliases' | translate }}" matTooltip="{{ 'entity.aliases' | translate }}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="openEntityAliases($event)"> (click)="openEntityAliases($event)">
<mat-icon>devices_other</mat-icon> <mat-icon>devices_other</mat-icon>
</button> </button>
<button [fxShow]="isEdit" mat-button mat-icon-button <button [fxShow]="isEdit" mat-icon-button
matTooltip="{{ 'dashboard.settings' | translate }}" matTooltip="{{ 'dashboard.settings' | translate }}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="openDashboardSettings($event)"> (click)="openDashboardSettings($event)">
@ -89,23 +89,23 @@
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.gt-sm displayUserInfo="true"> <tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.gt-sm displayUserInfo="true">
</tb-user-menu> </tb-user-menu>
<div [fxShow]="isEdit" <div [fxShow]="isEdit"
fxFlex.xs fxFlex.sm fxLayout="row" fxFlex.lt-md fxLayout="row"
fxLayoutAlign.gt-sm="start center" fxLayoutAlign.gt-sm="start center"
fxLayoutAlign="space-between center" fxLayoutGap="12px"> fxLayoutAlign="end center" fxLayoutGap="12px">
<button mat-button mat-icon-button <button mat-icon-button
matTooltip="{{'dashboard.manage-states' | translate}}" matTooltip="{{'dashboard.manage-states' | translate}}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="manageDashboardStates($event)"> (click)="manageDashboardStates($event)">
<mat-icon>layers</mat-icon> <mat-icon>layers</mat-icon>
</button> </button>
<button mat-button mat-icon-button <button mat-icon-button
matTooltip="{{'layout.manage' | translate}}" matTooltip="{{'layout.manage' | translate}}"
matTooltipPosition="below" matTooltipPosition="below"
(click)="manageDashboardLayouts($event)"> (click)="manageDashboardLayouts($event)">
<mat-icon>view_compact</mat-icon> <mat-icon>view_compact</mat-icon>
</button> </button>
</div> </div>
<tb-states-component fxFlex.xs fxFlex.sm <tb-states-component fxFlex.lt-md
[statesControllerId]="isEdit ? 'default' : dashboardConfiguration.settings.stateControllerId" [statesControllerId]="isEdit ? 'default' : dashboardConfiguration.settings.stateControllerId"
[dashboardCtrl]="this" [dashboardCtrl]="this"
[dashboardId]="dashboard.id ? dashboard.id.id : ''" [dashboardId]="dashboard.id ? dashboard.id.id : ''"

20
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.html

@ -16,7 +16,7 @@
--> -->
<form #settingsForm="ngForm" (ngSubmit)="save()"> <form #settingsForm="ngForm" (ngSubmit)="save()">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2> <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -28,17 +28,17 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar> </mat-progress-bar>
<div mat-dialog-content> <div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async"> <fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="16px">
<div *ngIf="settings" [formGroup]="settingsFormGroup"> <div *ngIf="settings" [formGroup]="settingsFormGroup">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>dashboard.state-controller</mat-label> <mat-label translate>dashboard.state-controller</mat-label>
<mat-select required matInput formControlName="stateControllerId"> <mat-select required formControlName="stateControllerId">
<mat-option *ngFor="let stateControllerId of stateControllerIds" [value]="stateControllerId"> <mat-option *ngFor="let stateControllerId of stateControllerIds" [value]="stateControllerId">
{{stateControllerId}} {{stateControllerId}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">
<mat-checkbox fxFlex formControlName="toolbarAlwaysOpen"> <mat-checkbox fxFlex formControlName="toolbarAlwaysOpen">
{{ 'dashboard.toolbar-always-open' | translate }} {{ 'dashboard.toolbar-always-open' | translate }}
</mat-checkbox> </mat-checkbox>
@ -52,7 +52,7 @@
formControlName="titleColor"> formControlName="titleColor">
</tb-color-input> </tb-color-input>
</div> </div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">
<mat-checkbox fxFlex formControlName="showDashboardsSelect"> <mat-checkbox fxFlex formControlName="showDashboardsSelect">
{{ 'dashboard.display-dashboards-selection' | translate }} {{ 'dashboard.display-dashboards-selection' | translate }}
</mat-checkbox> </mat-checkbox>
@ -117,7 +117,7 @@
</tb-image-input> </tb-image-input>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>dashboard.background-size-mode</mat-label> <mat-label translate>dashboard.background-size-mode</mat-label>
<mat-select matInput formControlName="backgroundSizeMode"> <mat-select formControlName="backgroundSizeMode">
<mat-option value="100%">Fit width</mat-option> <mat-option value="100%">Fit width</mat-option>
<mat-option value="auto 100%">Fit height</mat-option> <mat-option value="auto 100%">Fit height</mat-option>
<mat-option value="cover">Cover</mat-option> <mat-option value="cover">Cover</mat-option>
@ -126,7 +126,7 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<small translate>dashboard.mobile-layout</small> <small translate>dashboard.mobile-layout</small>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs fxLayoutGap="8px" style="margin-top: 8px">
<mat-checkbox fxFlex formControlName="mobileAutoFillHeight"> <mat-checkbox fxFlex formControlName="mobileAutoFillHeight">
{{ 'dashboard.autofill-height' | translate }} {{ 'dashboard.autofill-height' | translate }}
</mat-checkbox> </mat-checkbox>
@ -148,16 +148,14 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid [disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid
|| (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)"> || (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

14
common/dao-api/src/main/java/org/thingsboard/server/dao/util/NoSqlDao.java → ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.scss

@ -13,10 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.dao.util; :host {
small {
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; white-space: normal;
}
}
@ConditionalOnProperty(prefix = "database.entities", value = "type", havingValue = "cassandra") :host ::ng-deep {
public @interface NoSqlDao { .mat-checkbox-label {
white-space: normal;
}
} }

2
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.ts

@ -38,7 +38,7 @@ export interface DashboardSettingsDialogData {
selector: 'tb-dashboard-settings-dialog', selector: 'tb-dashboard-settings-dialog',
templateUrl: './dashboard-settings-dialog.component.html', templateUrl: './dashboard-settings-dialog.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: DashboardSettingsDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: DashboardSettingsDialogComponent}],
styleUrls: [] styleUrls: ['./dashboard-settings-dialog.component.scss']
}) })
export class DashboardSettingsDialogComponent extends DialogComponent<DashboardSettingsDialogComponent, DashboardSettingsDialogData> export class DashboardSettingsDialogComponent extends DialogComponent<DashboardSettingsDialogComponent, DashboardSettingsDialogData>
implements OnInit, ErrorStateMatcher { implements OnInit, ErrorStateMatcher {

6
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-toolbar.component.html

@ -15,12 +15,10 @@
limitations under the License. limitations under the License.
--> -->
<mat-fab-toolbar [isOpen]="toolbarOpened" <mat-fab-toolbar [isOpen]="toolbarOpened" color="primary" direction="left"
color="primary"
direction="left"
[ngClass]="{'is-fullscreen': forceFullscreen, 'mat-elevation-z1': forceFullscreen && toolbarOpened }"> [ngClass]="{'is-fullscreen': forceFullscreen, 'mat-elevation-z1': forceFullscreen && toolbarOpened }">
<mat-fab-trigger> <mat-fab-trigger>
<button mat-button mat-icon-button mat-fab color="primary" <button mat-fab color="primary"
[matTooltip]="!toolbarOpened ? ('dashboard.open-toolbar' | translate) : ''" [matTooltip]="!toolbarOpened ? ('dashboard.open-toolbar' | translate) : ''"
matTooltipPosition="below" matTooltipPosition="below"
(click)="onTriggerClick()"> (click)="onTriggerClick()">

29
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-toolbar.component.scss

@ -24,29 +24,12 @@ $mobile-toolbar-height-total: 84px !default;
tb-dashboard-toolbar { tb-dashboard-toolbar {
mat-fab-toolbar { mat-fab-toolbar {
mat-fab-trigger { mat-fab-trigger {
.mat-button { .mat-fab {
&.mat-fab { width: 36px;
width: 36px; height: 36px;
height: 36px; margin: 4px 0 0 4px;
margin: 4px 0 0 4px; .mat-button-wrapper {
line-height: 36px; padding: 0;
.mat-button-wrapper {
width: 36px;
height: 36px;
padding: 0 6px;
box-sizing: border-box;
}
mat-icon {
display: block;
position: absolute;
top: 25%;
width: 18px;
min-width: 18px;
height: 18px;
min-height: 18px;
margin: 0;
line-height: 18px;
}
} }
} }
} }

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

@ -28,7 +28,7 @@ import {
WidgetContextMenuItem WidgetContextMenuItem
} from '@home/models/dashboard-component.models'; } from '@home/models/dashboard-component.models';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { Hotkey } from 'angular2-hotkeys';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ItemBufferService } from '@app/core/services/item-buffer.service'; import { ItemBufferService } from '@app/core/services/item-buffer.service';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser';

3
ui-ngx/src/app/modules/home/pages/dashboard/layout/layout.models.ts

@ -14,9 +14,6 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Widget } from '@shared/models/widget.models';
import { WidgetLayout } from '@shared/models/dashboard.models';
export interface ILayoutController { export interface ILayoutController {
reload(); reload();
resetHighlight(); resetHighlight();

27
ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.html

@ -16,7 +16,7 @@
--> -->
<form #widgetForm="ngForm" [formGroup]="layoutsFormGroup" (ngSubmit)="save()"> <form #widgetForm="ngForm" [formGroup]="layoutsFormGroup" (ngSubmit)="save()">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>layout.manage</h2> <h2 translate>layout.manage</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -38,33 +38,32 @@
</mat-checkbox> </mat-checkbox>
</div> </div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<button fxFlex fxLayout="column" <button fxFlex
type="button" mat-button mat-raised-button color="primary" type="button" mat-raised-button color="primary"
class="tb-layout-button" class="tb-layout-button"
(click)="openLayoutSettings('main')"> (click)="openLayoutSettings('main')">
<span translate>layout.main</span> <span translate>layout.main</span>
</button> </button>
<button fxFlex fxLayout="column" [fxShow]="layoutsFormGroup.get('right').value" <button fxFlex [fxShow]="layoutsFormGroup.get('right').value"
type="button" mat-button mat-raised-button color="primary" type="button" mat-raised-button color="primary"
class="tb-layout-button" class="tb-layout-button"
(click)="openLayoutSettings('right')"> (click)="openLayoutSettings('right')">
<span translate>layout.right</span> <span translate>layout.right</span>
</button> </button>
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty"> [disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button
style="margin-right: 20px;" color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
</div> </div>

11
ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.ts

@ -16,22 +16,17 @@
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { Widget, widgetTypesData } from '@shared/models/widget.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityService } from '@core/http/entity.service'; import { DashboardLayoutId, DashboardStateLayouts } from '@app/shared/models/dashboard.models';
import { Dashboard, DashboardLayoutId, DashboardStateLayouts } from '@app/shared/models/dashboard.models'; import { deepClone, isDefined } from '@core/utils';
import { IAliasController } from '@core/api/widget-api.models';
import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-component.models';
import { deepClone, isDefined, isString } from '@core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { MatDialog } from '@angular/material/dialog';
import { import {
DashboardSettingsDialogComponent, DashboardSettingsDialogComponent,
DashboardSettingsDialogData DashboardSettingsDialogData

10
ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<form #stateForm="ngForm" [formGroup]="stateFormGroup" (ngSubmit)="save()" style="min-width: 600px;"> <form #stateForm="ngForm" [formGroup]="stateFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>{{ isAdd ? 'dashboard.add-state' : 'dashboard.edit-state' }}</h2> <h2 translate>{{ isAdd ? 'dashboard.add-state' : 'dashboard.edit-state' }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -51,15 +51,13 @@
</mat-checkbox> </mat-checkbox>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty"> [disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }} {{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

4
ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.ts

@ -31,7 +31,6 @@ import {
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { DashboardState } from '@app/shared/models/dashboard.models'; import { DashboardState } from '@app/shared/models/dashboard.models';
import { MatDialog } from '@angular/material/dialog';
import { DashboardStateInfo } from '@home/pages/dashboard/states/manage-dashboard-states-dialog.component.models'; import { DashboardStateInfo } from '@home/pages/dashboard/states/manage-dashboard-states-dialog.component.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
@ -71,8 +70,7 @@ export class DashboardStateDialogComponent extends
public dialogRef: MatDialogRef<DashboardStateDialogComponent, DashboardStateInfo>, public dialogRef: MatDialogRef<DashboardStateDialogComponent, DashboardStateInfo>,
private fb: FormBuilder, private fb: FormBuilder,
private translate: TranslateService, private translate: TranslateService,
private dashboardUtils: DashboardUtilsService, private dashboardUtils: DashboardUtilsService) {
private dialog: MatDialog) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.states = this.data.states; this.states = this.data.states;

2
ui-ngx/src/app/modules/home/pages/dashboard/states/entity-state-controller.component.ts

@ -201,7 +201,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
const entityName = targetParams && targetParams.entityName ? targetParams.entityName : ''; const entityName = targetParams && targetParams.entityName ? targetParams.entityName : '';
const entityLabel = targetParams && targetParams.entityLabel ? targetParams.entityLabel : ''; const entityLabel = targetParams && targetParams.entityLabel ? targetParams.entityLabel : '';
result = this.utils.insertVariable(stateName, 'entityName', entityName); result = this.utils.insertVariable(stateName, 'entityName', entityName);
result = this.utils.insertVariable(stateName, 'entityLabel', entityLabel); result = this.utils.insertVariable(result, 'entityLabel', entityLabel);
for (const prop of Object.keys(params)) { for (const prop of Object.keys(params)) {
if (params[prop] && params[prop].entityName) { if (params[prop] && params[prop].entityName) {
result = this.utils.insertVariable(result, prop + ':entityName', params[prop].entityName); result = this.utils.insertVariable(result, prop + ':entityName', params[prop].entityName);

30
ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<form #statesForm="ngForm" [formGroup]="statesFormGroup" (ngSubmit)="save()" style="min-width: 620px;"> <form #statesForm="ngForm" [formGroup]="statesFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar color="primary">
<h2 translate>dashboard.manage-states</h2> <h2 translate>dashboard.manage-states</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
@ -36,14 +36,14 @@
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<span class="tb-entity-table-title" translate>dashboard.states</span> <span class="tb-entity-table-title" translate>dashboard.states</span>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
(click)="addState($event)" (click)="addState($event)"
matTooltip="{{ 'dashboard.add-state' | translate }}" matTooltip="{{ 'dashboard.add-state' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>
<button mat-button mat-icon-button [disabled]="isLoading$ | async" (click)="enterFilterMode()" <button mat-icon-button [disabled]="isLoading$ | async" (click)="enterFilterMode()"
type="button" type="button"
matTooltip="{{ 'action.search' | translate }}" matTooltip="{{ 'action.search' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -53,7 +53,7 @@
</mat-toolbar> </mat-toolbar>
<mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode"> <mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<button mat-button mat-icon-button <button mat-icon-button
type="button" type="button"
matTooltip="{{ 'dashboard.search-states' | translate }}" matTooltip="{{ 'dashboard.search-states' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -66,7 +66,7 @@
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
placeholder="{{ 'dashboard.search-states' | translate }}"/> placeholder="{{ 'dashboard.search-states' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"
type="button" type="button"
matTooltip="{{ 'action.close' | translate }}" matTooltip="{{ 'action.close' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -75,16 +75,16 @@
</div> </div>
</mat-toolbar> </mat-toolbar>
<div class="table-container"> <div class="table-container">
<mat-table [dataSource]="dataSource" <table mat-table [dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 60%"> {{ 'dashboard.state-name' | translate }} </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 0 0 60%"> {{ 'dashboard.state-name' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let state"> <mat-cell *matCellDef="let state">
{{ state.name }} {{ state.name }}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<ng-container matColumnDef="id"> <ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 40%"> {{ 'dashboard.state-id' | translate }} </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 0 0 40%"> {{ 'dashboard.state-id' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let state"> <mat-cell *matCellDef="let state">
{{ state.id }} {{ state.id }}
</mat-cell> </mat-cell>
@ -100,14 +100,14 @@
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let state"> <mat-cell *matCellDef="let state">
<div fxFlex fxLayout="row"> <div fxFlex fxLayout="row">
<button mat-button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
matTooltip="{{ 'dashboard.edit-state' | translate }}" matTooltip="{{ 'dashboard.edit-state' | translate }}"
matTooltipPosition="above" matTooltipPosition="above"
(click)="editState($event, state)"> (click)="editState($event, state)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button [fxShow]="!state.root" mat-button mat-icon-button [disabled]="isLoading$ | async" <button [fxShow]="!state.root" mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
matTooltip="{{ 'dashboard.delete-state' | translate }}" matTooltip="{{ 'dashboard.delete-state' | translate }}"
matTooltipPosition="above" matTooltipPosition="above"
@ -120,7 +120,7 @@
<mat-header-row class="mat-row-select" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <mat-header-row class="mat-row-select" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row class="mat-row-select" <mat-row class="mat-row-select"
*matRowDef="let state; columns: displayedColumns;"></mat-row> *matRowDef="let state; columns: displayedColumns;"></mat-row>
</mat-table> </table>
<span [fxShow]="dataSource.isEmpty() | async" <span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"
class="no-data-found" translate>{{ 'dashboard.no-states-text' }}</span> class="no-data-found" translate>{{ 'dashboard.no-states-text' }}</span>
@ -135,15 +135,13 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayoutAlign="end center">
<span fxFlex></span> <button mat-raised-button color="primary"
<button mat-button mat-raised-button color="primary"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty"> [disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

8
ui-ngx/src/app/modules/home/pages/dashboard/states/states-component.directive.ts

@ -17,20 +17,16 @@
import { import {
ComponentRef, ComponentRef,
Directive, Directive,
ElementRef,
Input, Input,
OnChanges, OnChanges,
OnInit,
OnDestroy, OnDestroy,
OnInit,
SimpleChanges, SimpleChanges,
ViewContainerRef, ViewContainerRef
ChangeDetectorRef
} from '@angular/core'; } from '@angular/core';
import { DashboardPageComponent } from '@home/pages/dashboard/dashboard-page.component';
import { DashboardState } from '@shared/models/dashboard.models'; import { DashboardState } from '@shared/models/dashboard.models';
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models'; import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { IStateController } from '@core/api/widget-api.models';
import { IStateControllerComponent } from '@home/pages/dashboard/states/state-controller.models'; import { IStateControllerComponent } from '@home/pages/dashboard/states/state-controller.models';
@Directive({ @Directive({

32
ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html

@ -34,23 +34,23 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<span fxFlex></span> <span fxFlex></span>
<button mat-button fxHide.xs fxHide.sm [disabled]="!iframeWidgetEditModeInited" <button mat-button fxHide.lt-md [disabled]="!iframeWidgetEditModeInited"
(click)="applyWidgetScript()" (click)="applyWidgetScript()"
matTooltip="{{ 'widget.run' | translate }} (CTRL + Return)" matTooltip="{{ 'widget.run' | translate }} (CTRL + Return)"
matTooltipPosition="below"> matTooltipPosition="below">
<mat-icon>play_arrow</mat-icon> <mat-icon>play_arrow</mat-icon>
<span translate>action.run</span> <span translate>action.run</span>
</button> </button>
<button mat-button mat-raised-button <button mat-raised-button
fxHide.xs fxHide.sm [disabled]="(isLoading$ | async) || undoDisabled()" fxHide.lt-md [disabled]="(isLoading$ | async) || undoDisabled()"
(click)="undoWidget()" (click)="undoWidget()"
matTooltip="{{ 'widget.undo' | translate }} (CTRL + Q)" matTooltip="{{ 'widget.undo' | translate }} (CTRL + Q)"
matTooltipPosition="below"> matTooltipPosition="below">
<mat-icon>undo</mat-icon> <mat-icon>undo</mat-icon>
<span translate>action.undo</span> <span translate>action.undo</span>
</button> </button>
<button *ngIf="!isReadOnly" mat-button mat-raised-button <button *ngIf="!isReadOnly" mat-raised-button
fxHide.xs fxHide.sm [disabled]="(isLoading$ | async) || saveDisabled()" fxHide.lt-md [disabled]="(isLoading$ | async) || saveDisabled()"
(click)="saveWidget()" (click)="saveWidget()"
[tb-circular-progress]="saveWidgetPending" [tb-circular-progress]="saveWidgetPending"
matTooltip="{{ 'widget.save' | translate }} (CTRL + S)" matTooltip="{{ 'widget.save' | translate }} (CTRL + S)"
@ -58,8 +58,8 @@
<mat-icon>save</mat-icon> <mat-icon>save</mat-icon>
<span translate>action.save</span> <span translate>action.save</span>
</button> </button>
<button mat-button mat-raised-button <button mat-raised-button
fxHide.xs fxHide.sm [disabled]="(isLoading$ | async) || saveAsDisabled()" fxHide.lt-md [disabled]="(isLoading$ | async) || saveAsDisabled()"
(click)="saveWidgetAs()" (click)="saveWidgetAs()"
[tb-circular-progress]="saveWidgetAsPending" [tb-circular-progress]="saveWidgetAsPending"
matTooltip="{{ 'widget.saveAs' | translate }} (Shift + CTRL + S)" matTooltip="{{ 'widget.saveAs' | translate }} (Shift + CTRL + S)"
@ -68,14 +68,14 @@
<span translate>action.saveAs</span> <span translate>action.saveAs</span>
</button> </button>
<button mat-button <button mat-button
fxHide.xs fxHide.sm fxHide.lt-md
(click)="fullscreen = !fullscreen" (click)="fullscreen = !fullscreen"
matTooltip="{{ 'widget.toggle-fullscreen' | translate }} (Shift + CTRL + F)" matTooltip="{{ 'widget.toggle-fullscreen' | translate }} (Shift + CTRL + F)"
matTooltipPosition="below"> matTooltipPosition="below">
<mat-icon>{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> <mat-icon>{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
<span translate>widget.toggle-fullscreen</span> <span translate>widget.toggle-fullscreen</span>
</button> </button>
<button mat-button mat-icon-button fxHide.gt-sm <button mat-icon-button fxHide.gt-sm
[matMenuTriggerFor]="widgetEditMenu"> [matMenuTriggerFor]="widgetEditMenu">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
@ -123,7 +123,7 @@
(ngModelChange)="isDirty = true" (ngModelChange)="isDirty = true"
placeholder="{{ 'widget.resource-url' | translate }}"/> placeholder="{{ 'widget.resource-url' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-button mat-icon-button color="primary" <button mat-icon-button color="primary"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="removeResource(i)" (click)="removeResource(i)"
matTooltip="{{'widget.remove-resource' | translate}}" matTooltip="{{'widget.remove-resource' | translate}}"
@ -132,7 +132,7 @@
</button> </button>
</div> </div>
<div style="margin-top: 6px;"> <div style="margin-top: 6px;">
<button mat-button mat-raised-button color="primary" <button mat-raised-button color="primary"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="addResource()" (click)="addResource()"
matTooltip="{{'widget.add-resource' | translate}}" matTooltip="{{'widget.add-resource' | translate}}"
@ -165,7 +165,7 @@
<button mat-button (click)="beautifyCss()"> <button mat-button (click)="beautifyCss()">
{{ 'widget.tidy' | translate }} {{ 'widget.tidy' | translate }}
</button> </button>
<button mat-button mat-icon-button class="tb-mat-32" <button mat-icon-button class="tb-mat-32"
(click)="cssFullscreen = !cssFullscreen" (click)="cssFullscreen = !cssFullscreen"
matTooltip="{{(cssFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(cssFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -185,7 +185,7 @@
<button mat-button (click)="beautifyJson()"> <button mat-button (click)="beautifyJson()">
{{ 'widget.tidy' | translate }} {{ 'widget.tidy' | translate }}
</button> </button>
<button mat-button mat-icon-button class="tb-mat-32" <button mat-icon-button class="tb-mat-32"
(click)="jsonSettingsFullscreen = !jsonSettingsFullscreen" (click)="jsonSettingsFullscreen = !jsonSettingsFullscreen"
matTooltip="{{(jsonSettingsFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(jsonSettingsFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -201,7 +201,7 @@
<button mat-button (click)="beautifyDataKeyJson()"> <button mat-button (click)="beautifyDataKeyJson()">
{{ 'widget.tidy' | translate }} {{ 'widget.tidy' | translate }}
</button> </button>
<button mat-button mat-icon-button class="tb-mat-32" <button mat-icon-button class="tb-mat-32"
(click)="jsonDataKeySettingsFullscreen = !jsonDataKeySettingsFullscreen" (click)="jsonDataKeySettingsFullscreen = !jsonDataKeySettingsFullscreen"
matTooltip="{{(jsonDataKeySettingsFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(jsonDataKeySettingsFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -222,7 +222,7 @@
<button mat-button (click)="beautifyJs()"> <button mat-button (click)="beautifyJs()">
{{ 'widget.tidy' | translate }} {{ 'widget.tidy' | translate }}
</button> </button>
<button mat-button mat-icon-button class="tb-mat-32" <button mat-icon-button class="tb-mat-32"
(click)="javascriptFullscreen = !javascriptFullscreen" (click)="javascriptFullscreen = !javascriptFullscreen"
matTooltip="{{(javascriptFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(javascriptFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -240,7 +240,7 @@
<div tb-fullscreen [fullscreen]="iFrameFullscreen" style="width: 100%; height: 100%;"> <div tb-fullscreen [fullscreen]="iFrameFullscreen" style="width: 100%; height: 100%;">
<iframe #widgetIFrame frameborder="0" height="100%" width="100%"></iframe> <iframe #widgetIFrame frameborder="0" height="100%" width="100%"></iframe>
<div style="position: absolute; top: 10px; left: 10px; bottom: initial;"> <div style="position: absolute; top: 10px; left: 10px; bottom: initial;">
<button mat-button mat-icon-button <button mat-icon-button
class="tb-fullscreen-button-style" class="tb-fullscreen-button-style"
(click)="iFrameFullscreen = !iFrameFullscreen" (click)="iFrameFullscreen = !iFrameFullscreen"
matTooltip="{{(iFrameFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" matTooltip="{{(iFrameFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"

7
ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts

@ -21,7 +21,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { toWidgetInfo, WidgetInfo } from '@home/models/widget-component.models'; import { toWidgetInfo, WidgetInfo } from '@home/models/widget-component.models';
import { WidgetConfig, widgetType, WidgetType, widgetTypesData, Widget } from '@shared/models/widget.models'; import { Widget, WidgetConfig, WidgetType, widgetType, widgetTypesData } from '@shared/models/widget.models';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard'; import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard';
@ -29,7 +29,7 @@ import { AuthUser } from '@shared/models/user.model';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { Hotkey } from 'angular2-hotkeys';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors'; import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors';
import * as ace from 'ace-builds'; import * as ace from 'ace-builds';
@ -38,15 +38,14 @@ import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { WINDOW } from '@core/services/window.service'; import { WINDOW } from '@core/services/window.service';
import { WindowMessage } from '@shared/models/window-message.model'; import { WindowMessage } from '@shared/models/window-message.model';
import { ExceptionData } from '@shared/models/error.models'; import { ExceptionData } from '@shared/models/error.models';
import Timeout = NodeJS.Timeout;
import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component';
import { import {
SaveWidgetTypeAsDialogComponent, SaveWidgetTypeAsDialogComponent,
SaveWidgetTypeAsDialogResult SaveWidgetTypeAsDialogResult
} from '@home/pages/widget/save-widget-type-as-dialog.component'; } from '@home/pages/widget/save-widget-type-as-dialog.component';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import Timeout = NodeJS.Timeout;
// @dynamic // @dynamic
@Component({ @Component({

6
ui-ngx/src/app/shared/components/color-input.component.scss

@ -18,3 +18,9 @@
width: 100%; width: 100%;
} }
} }
:host ::ng-deep {
.mat-form-field-infix{
width: 150px;
}
}

22
ui-ngx/src/app/shared/components/color-input.component.ts

@ -14,30 +14,12 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { DataKey, DatasourceType } from '@shared/models/widget.models'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator,
Validators
} from '@angular/forms';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { EntityService } from '@core/http/entity.service';
import { DataKeysCallbacks } from '@home/components/widget/data-keys.component.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { Observable, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { alarmFields } from '@shared/models/alarm.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';

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

@ -14,22 +14,21 @@
/// limitations under the License. /// limitations under the License.
/// ///
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, 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 { map, mergeMap, startWith, tap } from 'rxjs/operators';
import {PageData, emptyPageData} 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 {DashboardId} from '@app/shared/models/id/dashboard-id'; import { DashboardService } from '@core/http/dashboard.service';
import {DashboardService} from '@core/http/dashboard.service'; 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 { getCurrentAuthUser } from '@app/core/auth/auth.selectors';
import {getCurrentAuthUser} from '@app/core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum';
import {Authority} from '@shared/models/authority.enum'; import { TranslateService } from '@ngx-translate/core';
import {TranslateService} from '@ngx-translate/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
@Component({ @Component({
selector: 'tb-dashboard-autocomplete', selector: 'tb-dashboard-autocomplete',

2
ui-ngx/src/app/shared/components/dashboard-select.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<mat-select fxHide.xs fxHide.sm fxHide.md <mat-select fxHide.lt-lg
[required]="required" [required]="required"
[disabled]="disabled" [disabled]="disabled"
[(ngModel)]="dashboardId" [(ngModel)]="dashboardId"

2
ui-ngx/src/app/shared/components/dialog/todo-dialog.component.ts

@ -24,7 +24,7 @@ import { MatDialogRef } from '@angular/material/dialog';
}) })
export class TodoDialogComponent { export class TodoDialogComponent {
comingSoon = require('../../../../assets/coming-soon.jpg'); comingSoon = require('../../../../assets/coming-soon.jpg').default;
constructor(public dialogRef: MatDialogRef<TodoDialogComponent>) { constructor(public dialogRef: MatDialogRef<TodoDialogComponent>) {
} }

39
ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts

@ -14,39 +14,18 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
AfterContentInit, import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
AfterViewInit, import { Observable, of } from 'rxjs';
Component, import { filter, map, mergeMap, share, tap } from 'rxjs/operators';
ElementRef, import { Store } from '@ngrx/store';
forwardRef, import { AppState } from '@app/core/core.state';
Input, import { TranslateService } from '@ngx-translate/core';
OnInit,
SkipSelf,
ViewChild
} from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
FormGroup,
FormGroupDirective,
NG_VALUE_ACCESSOR, NgForm, Validators
} from '@angular/forms';
import {Observable, of} from 'rxjs';
import {map, mergeMap, startWith, tap, share, pairwise, filter} from 'rxjs/operators';
import {Store} from '@ngrx/store';
import {AppState} from '@app/core/core.state';
import {TranslateService} from '@ngx-translate/core';
import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import {BaseData} from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service';
import {EntityId} from '@shared/models/id/entity-id'; import { MatAutocomplete } from '@angular/material/autocomplete';
import {EntityService} from '@core/http/entity.service';
import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipList } from '@angular/material/chips'; import { MatChipList } from '@angular/material/chips';
import { ErrorStateMatcher } from '@angular/material/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { emptyPageData } from '@shared/models/page/page-data';
interface EntityTypeInfo { interface EntityTypeInfo {
name: string; name: string;

11
ui-ngx/src/app/shared/components/fab-toolbar.component.ts

@ -15,19 +15,20 @@
/// ///
import { import {
AfterViewInit,
Component, Component,
Directive,
ElementRef, ElementRef,
Inject,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
Renderer2,
ViewEncapsulation,
SimpleChanges, SimpleChanges,
Inject, AfterViewInit, AfterViewChecked, Directive, OnDestroy ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { WINDOW } from '@core/services/window.service'; import { WINDOW } from '@core/services/window.service';
import { mixinColor, CanColorCtor } from '@angular/material/core'; import { CanColorCtor, mixinColor } from '@angular/material/core';
export declare type FabToolbarDirection = 'left' | 'right'; export declare type FabToolbarDirection = 'left' | 'right';

1
ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx

@ -33,7 +33,6 @@ class ThingsboardCheckbox extends React.Component<JsonFormFieldProps, JsonFormFi
onChange={(e, checked) => { onChange={(e, checked) => {
this.props.onChangeValidate(e); this.props.onChangeValidate(e);
}} }}
style={{paddingTop: '14px'}}
/> />
} }
label={this.props.form.title} label={this.props.form.title}

7
ui-ngx/src/app/shared/components/led-light.component.ts

@ -16,6 +16,7 @@
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael';
import Raphael from 'raphael'; import Raphael from 'raphael';
import * as tinycolor_ from 'tinycolor2'; import * as tinycolor_ from 'tinycolor2';
@ -38,7 +39,7 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
@Input() colorOff: string; @Input() colorOff: string;
@Input() offOpacity: string; @Input() offOpacity: number;
private enabledValue: boolean; private enabledValue: boolean;
get enabled(): boolean { get enabled(): boolean {
@ -61,7 +62,7 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
} }
ngOnInit(): void { ngOnInit(): void {
this.offOpacity = this.offOpacity || '0.4'; this.offOpacity = this.offOpacity || 0.4;
this.glowColor = tinycolor(this.colorOn).lighten().toHexString(); this.glowColor = tinycolor(this.colorOn).lighten().toHexString();
} }
@ -100,7 +101,7 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
if (this.enabled) { if (this.enabled) {
this.circleElement.attr('fill', this.colorOn); this.circleElement.attr('fill', this.colorOn);
this.circleElement.attr('stroke', this.colorOn); this.circleElement.attr('stroke', this.colorOn);
this.circleElement.attr('opacity', '1'); this.circleElement.attr('opacity', 1);
if (this.circleElement.theGlow) { if (this.circleElement.theGlow) {
this.circleElement.theGlow.remove(); this.circleElement.theGlow.remove();
} }

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

@ -23,7 +23,7 @@ import { Component } from '@angular/core';
}) })
export class LogoComponent { export class LogoComponent {
logo = require('../../../assets/logo_title_white.svg'); logo = require('../../../assets/logo_title_white.svg').default;
gotoThingsboard(): void { gotoThingsboard(): void {
window.open('https://thingsboard.io', '_blank'); window.open('https://thingsboard.io', '_blank');

2
ui-ngx/src/app/shared/components/material-icon-select.component.html

@ -17,7 +17,7 @@
--> -->
<div fxLayout="row" [formGroup]="materialIconFormGroup"> <div fxLayout="row" [formGroup]="materialIconFormGroup">
<mat-icon (click)="openIconDialog()">{{materialIconFormGroup.get('icon').value}}</mat-icon> <mat-icon (click)="openIconDialog()">{{materialIconFormGroup.get('icon').value}}</mat-icon>
<mat-form-field> <mat-form-field fxFlex>
<mat-label translate>icon.icon</mat-label> <mat-label translate>icon.icon</mat-label>
<input matInput formControlName="icon" (mousedown)="openIconDialog()"> <input matInput formControlName="icon" (mousedown)="openIconDialog()">
</mat-form-field> </mat-form-field>

6
ui-ngx/src/app/shared/components/material-icon-select.component.scss

@ -21,3 +21,9 @@
border: solid 1px rgba(0, 0, 0, .27); border: solid 1px rgba(0, 0, 0, .27);
} }
} }
:host ::ng-deep {
.mat-form-field-infix{
width: 146px;
}
}

24
ui-ngx/src/app/shared/components/material-icon-select.component.ts

@ -14,31 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { DataKey, DatasourceType } from '@shared/models/widget.models'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator,
Validators
} from '@angular/forms';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { EntityService } from '@core/http/entity.service';
import { DataKeysCallbacks } from '@home/components/widget/data-keys.component.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { Observable, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { alarmFields } from '@shared/models/alarm.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
@Component({ @Component({

2
ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html

@ -24,7 +24,7 @@
[matAutocomplete]="relationTypeAutocomplete"> [matAutocomplete]="relationTypeAutocomplete">
<button *ngIf="relationTypeFormGroup.get('relationType').value && !disabled" <button *ngIf="relationTypeFormGroup.get('relationType').value && !disabled"
type="button" type="button"
matSuffix mat-button mat-icon-button aria-label="Clear" matSuffix mat-icon-button aria-label="Clear"
(click)="clear()"> (click)="clear()">
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>

28
ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts

@ -14,27 +14,15 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild, OnDestroy} from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {Observable, of, throwError, Subscription} from 'rxjs'; import { Observable, of } from 'rxjs';
import {PageLink} from '@shared/models/page/page-link'; import { map, mergeMap, tap } from 'rxjs/operators';
import {Direction} from '@shared/models/page/sort-order'; import { Store } from '@ngrx/store';
import {filter, map, mergeMap, publishReplay, refCount, startWith, tap, publish} from 'rxjs/operators'; import { AppState } from '@app/core/core.state';
import {PageData, emptyPageData} from '@shared/models/page/page-data'; import { TranslateService } from '@ngx-translate/core';
import {DashboardInfo} from '@app/shared/models/dashboard.models'; import { BroadcastService } from '@app/core/services/broadcast.service';
import {DashboardId} from '@app/shared/models/id/dashboard-id'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {DashboardService} from '@core/http/dashboard.service';
import {Store} from '@ngrx/store';
import {AppState} from '@app/core/core.state';
import {getCurrentAuthUser} from '@app/core/auth/auth.selectors';
import {Authority} from '@shared/models/authority.enum';
import {TranslateService} from '@ngx-translate/core';
import {DeviceService} from '@core/http/device.service';
import {EntitySubtype, EntityType} from '@app/shared/models/entity-type.models';
import {BroadcastService} from '@app/core/services/broadcast.service';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {AssetService} from '@core/http/asset.service';
import {EntityViewService} from '@core/http/entity-view.service';
import { RelationTypes } from '@app/shared/models/relation.models'; import { RelationTypes } from '@app/shared/models/relation.models';
@Component({ @Component({

8
ui-ngx/src/app/shared/components/socialshare-panel.component.html

@ -16,7 +16,7 @@
--> -->
<div fxLayout="row" fxLayoutGap="12px" [fxHide]="isShareLinkLocal()"> <div fxLayout="row" fxLayoutGap="12px" [fxHide]="isShareLinkLocal()">
<button mat-button mat-icon-button mat-raised-button class="mat-primary" <button mat-icon-button mat-raised-button class="mat-primary"
shareButton="facebook" shareButton="facebook"
title="{{ shareTitle }}" title="{{ shareTitle }}"
description="{{ shareText }}" description="{{ shareText }}"
@ -25,7 +25,7 @@
matTooltip="{{ 'action.share-via' | translate:{provider:'Facebook'} }}"> matTooltip="{{ 'action.share-via' | translate:{provider:'Facebook'} }}">
<mat-icon svgIcon="mdi:facebook"></mat-icon> <mat-icon svgIcon="mdi:facebook"></mat-icon>
</button> </button>
<button mat-button mat-icon-button mat-raised-button class="mat-primary" <button mat-icon-button mat-raised-button class="mat-primary"
shareButton="twitter" shareButton="twitter"
title="{{ shareTitle }}" title="{{ shareTitle }}"
tags="{{ shareHashTags }}" tags="{{ shareHashTags }}"
@ -34,7 +34,7 @@
matTooltip="{{ 'action.share-via' | translate:{provider:'Twitter'} }}"> matTooltip="{{ 'action.share-via' | translate:{provider:'Twitter'} }}">
<mat-icon svgIcon="mdi:twitter"></mat-icon> <mat-icon svgIcon="mdi:twitter"></mat-icon>
</button> </button>
<button mat-button mat-icon-button mat-raised-button class="mat-primary" <button mat-icon-button mat-raised-button class="mat-primary"
shareButton="linkedin" shareButton="linkedin"
title="{{ shareTitle }}" title="{{ shareTitle }}"
url="{{ shareLink }}" url="{{ shareLink }}"
@ -42,7 +42,7 @@
matTooltip="{{ 'action.share-via' | translate:{provider:'Linkedin'} }}"> matTooltip="{{ 'action.share-via' | translate:{provider:'Linkedin'} }}">
<mat-icon svgIcon="mdi:linkedin"></mat-icon> <mat-icon svgIcon="mdi:linkedin"></mat-icon>
</button> </button>
<button mat-button mat-icon-button mat-raised-button class="mat-primary" <button mat-icon-button mat-raised-button class="mat-primary"
shareButton="reddit" shareButton="reddit"
title="{{ shareTitle }}" title="{{ shareTitle }}"
url="{{ shareLink }}" url="{{ shareLink }}"

12
ui-ngx/src/app/shared/components/socialshare-panel.component.ts

@ -14,16 +14,8 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { User } from '@shared/models/user.model'; import { isLocalUrl } from '@core/utils';
import { Authority } from '@shared/models/authority.enum';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { selectAuthUser, selectUserDetails } from '@core/auth/auth.selectors';
import { map } from 'rxjs/operators';
import { AuthService } from '@core/auth/auth.service';
import { Router } from '@angular/router';
import {isLocalUrl} from '@core/utils';
@Component({ @Component({
selector: 'tb-social-share-panel', selector: 'tb-social-share-panel',

1
ui-ngx/src/styles.scss

@ -863,6 +863,7 @@ mat-label {
.mat-dialog-container { .mat-dialog-container {
> *:first-child, form { > *:first-child, form {
min-width: 100% !important; min-width: 100% !important;
height: 100vh;
} }
.mat-dialog-content { .mat-dialog-content {
max-height: 100%; max-height: 100%;

18
ui/src/app/common/types.constant.js

@ -606,13 +606,29 @@ export default angular.module('thingsboard.types', [])
value: "modbus", value: "modbus",
name: "Modbus" name: "Modbus"
}, },
opc_ua: { opcua: {
value: "opcua", value: "opcua",
name: "OPC-UA" name: "OPC-UA"
}, },
ble: { ble: {
value: "ble", value: "ble",
name: "BLE" name: "BLE"
},
request: {
value: "request",
name: "Request"
},
can: {
value: "can",
name: "CAN"
},
bacnet: {
value: "bacnet",
name: "BACnet"
},
custom: {
value: "custom",
name: "Custom"
} }
}, },
gatewayLogLevel: { gatewayLogLevel: {

7
ui/src/app/components/gateway/gateway-config.directive.js

@ -37,7 +37,8 @@ function GatewayConfig() {
disabled: '=ngDisabled', disabled: '=ngDisabled',
gatewayConfig: '=', gatewayConfig: '=',
changeAlignment: '=', changeAlignment: '=',
theForm: '=' theForm: '=',
isReadOnly: '='
}, },
controller: GatewayConfigController, controller: GatewayConfigController,
controllerAs: 'vm', controllerAs: 'vm',
@ -83,6 +84,10 @@ function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window,
multiple: true, multiple: true,
}).then(function (config) { }).then(function (config) {
if (config && index > -1) { if (config && index > -1) {
console.log(config); //eslint-disable-line
if (!angular.equals(vm.gatewayConfig[index].config, config)) {
$scope.gatewayConfiguration.$setDirty();
}
vm.gatewayConfig[index].config = config; vm.gatewayConfig[index].config = config;
} }
}); });

12
ui/src/app/components/gateway/gateway-config.tpl.html

@ -18,7 +18,7 @@
<section name="gatewayConfig" layout="column" class="gateway-config"> <section name="gatewayConfig" layout="column" class="gateway-config">
<section layout="row" ng-form="gatewayConfig_{{$index}}" ng-repeat="connector in vm.gatewayConfig track by $index"> <section layout="row" ng-form="gatewayConfig_{{$index}}" ng-repeat="connector in vm.gatewayConfig track by $index">
<div layout="column" layout-align="center start" class="gateway-config-row"> <div layout="column" layout-align="center start" class="gateway-config-row">
<md-switch ng-model="connector.enabled" ng-disabled="gatewayConfig_{{$index}}.$invalid || vm.validateJSON(connector.config)" <md-switch ng-disabled="vm.isReadOnly" ng-model="connector.enabled" ng-disabled="gatewayConfig_{{$index}}.$invalid || vm.validateJSON(connector.config)"
aria-label="{{ 'gateway.connector-enabled' | translate }}"> aria-label="{{ 'gateway.connector-enabled' | translate }}">
</md-switch> </md-switch>
</div> </div>
@ -26,7 +26,7 @@
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
<md-input-container class="md-block" flex> <md-input-container class="md-block" flex>
<label>{{'gateway.connector-type' | translate }}</label> <label>{{'gateway.connector-type' | translate }}</label>
<md-select name="connectorType" <md-select ng-disabled="vm.isReadOnly" name="connectorType"
ng-change="vm.changeConnectorType(connector)" ng-change="vm.changeConnectorType(connector)"
aria-label="{{ 'gateway.gateway.connector-type' | translate }}" aria-label="{{ 'gateway.gateway.connector-type' | translate }}"
ng-model="connector.configType" required> ng-model="connector.configType" required>
@ -39,7 +39,7 @@
</div> </div>
</md-input-container> </md-input-container>
<md-input-container class="md-block" flex> <md-input-container class="md-block" flex>
<input placeholder="{{'gateway.connector-name' | translate }}" <input ng-disabled="vm.isReadOnly" placeholder="{{'gateway.connector-name' | translate }}"
ng-model-options="{ updateOn: 'blur' }" ng-model-options="{ updateOn: 'blur' }"
ng-change="vm.changeConnectorName(connector, $index)" name="connectorName" ng-model="connector.name" required/> ng-change="vm.changeConnectorName(connector, $index)" name="connectorName" ng-model="connector.name" required/>
<div ng-messages="vm.theForm.connectorName.$error"> <div ng-messages="vm.theForm.connectorName.$error">
@ -49,7 +49,7 @@
</div> </div>
<div layout="row" layout-align="end center" class="action-buttons" <div layout="row" layout-align="end center" class="action-buttons"
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}"> ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
<md-button class="md-icon-button md-mini" ng-click="vm.openConfigDialog($event, $index, connector.config, connector.name)" <md-button ng-disabled="vm.isReadOnly" class="md-icon-button md-mini" ng-click="vm.openConfigDialog($event, $index, connector.config, connector.name)"
aria-label="{{ 'gateway.update-config' | translate }}" aria-label="{{ 'gateway.update-config' | translate }}"
ng-class="{'md-warn': vm.validateJSON(connector.config)}"> ng-class="{'md-warn': vm.validateJSON(connector.config)}">
<md-icon class="material-icons">more_horiz</md-icon> <md-icon class="material-icons">more_horiz</md-icon>
@ -57,7 +57,7 @@
{{ 'gateway.update-config' | translate }} {{ 'gateway.update-config' | translate }}
</md-tooltip> </md-tooltip>
</md-button> </md-button>
<md-button class="md-icon-button md-mini" ng-click="vm.removeConnector($index)" <md-button ng-disabled="vm.isReadOnly" class="md-icon-button md-mini" ng-click="vm.removeConnector($index)"
aria-label="{{ 'gateway.delete' | translate }}"> aria-label="{{ 'gateway.delete' | translate }}">
<md-icon class="material-icons">close</md-icon> <md-icon class="material-icons">close</md-icon>
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -70,7 +70,7 @@
layout-align="center center" ng-class="{'disabled': vm.disabled}" layout-align="center center" ng-class="{'disabled': vm.disabled}"
class="no-data-found" translate>{{'gateway.no-connectors'}}</span> class="no-data-found" translate>{{'gateway.no-connectors'}}</span>
<div> <div>
<md-button class="md-raised" ng-click="vm.addNewConnector()" <md-button ng-show="!vm.isReadOnly" class="md-raised" ng-click="vm.addNewConnector()"
aria-label="{{ 'gateway.connector-add' | translate }}"> aria-label="{{ 'gateway.connector-add' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'gateway.connector-add' | translate }} {{ 'gateway.connector-add' | translate }}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save