Browse Source

Merge pull request #11838 from thingsboard/develop/flex-layout

Switch from flex-layout to pure CSS with tailwind.
pull/11814/head
Igor Kulikov 2 years ago
committed by GitHub
parent
commit
f907f01fa2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      application/src/main/data/json/demo/dashboards/firmware.json
  2. 10
      application/src/main/data/json/demo/dashboards/software.json
  3. 4
      application/src/main/data/json/demo/dashboards/thermostats.json
  4. 2
      application/src/main/data/json/system/widget_types/asset_admin_table.json
  5. 4
      application/src/main/data/json/system/widget_types/basic_gpio_control.json
  6. 4
      application/src/main/data/json/system/widget_types/basic_gpio_panel.json
  7. 2
      application/src/main/data/json/system/widget_types/device_admin_table.json
  8. 2
      application/src/main/data/json/system/widget_types/device_claiming_widget.json
  9. 4
      application/src/main/data/json/system/widget_types/raspberry_pi_gpio_control.json
  10. 4
      application/src/main/data/json/system/widget_types/raspberry_pi_gpio_panel.json
  11. 2
      application/src/main/data/json/system/widget_types/rpc_button.json
  12. 2
      application/src/main/data/json/system/widget_types/service_rpc.json
  13. 2
      application/src/main/data/json/system/widget_types/update_boolean_timeseries.json
  14. 2
      application/src/main/data/json/system/widget_types/update_device_attribute.json
  15. 2
      application/src/main/data/json/system/widget_types/update_double_timeseries.json
  16. 2
      application/src/main/data/json/system/widget_types/update_integer_timeseries.json
  17. 2
      application/src/main/data/json/system/widget_types/update_location_timeseries.json
  18. 2
      application/src/main/data/json/system/widget_types/update_server_boolean_attribute.json
  19. 2
      application/src/main/data/json/system/widget_types/update_server_date_attribute.json
  20. 2
      application/src/main/data/json/system/widget_types/update_server_double_attribute.json
  21. 2
      application/src/main/data/json/system/widget_types/update_server_image_attribute.json
  22. 2
      application/src/main/data/json/system/widget_types/update_server_integer_attribute.json
  23. 2
      application/src/main/data/json/system/widget_types/update_server_location_attribute.json
  24. 2
      application/src/main/data/json/system/widget_types/update_server_string_attribute.json
  25. 2
      application/src/main/data/json/system/widget_types/update_shared_boolean_attribute.json
  26. 2
      application/src/main/data/json/system/widget_types/update_shared_date_attribute.json
  27. 2
      application/src/main/data/json/system/widget_types/update_shared_double_attribute.json
  28. 2
      application/src/main/data/json/system/widget_types/update_shared_image_attribute.json
  29. 2
      application/src/main/data/json/system/widget_types/update_shared_integer_attribute.json
  30. 2
      application/src/main/data/json/system/widget_types/update_shared_location_attribute.json
  31. 2
      application/src/main/data/json/system/widget_types/update_shared_string_attribute.json
  32. 2
      application/src/main/data/json/system/widget_types/update_string_timeseries.json
  33. 12
      application/src/main/data/json/tenant/dashboards/gateways.json
  34. 9
      ui-ngx/.eslintrc.json
  35. 4
      ui-ngx/package.json
  36. 2
      ui-ngx/src/app/core/core.module.ts
  37. 9
      ui-ngx/src/app/core/services/dynamic-component-factory.service.ts
  38. 77
      ui-ngx/src/app/core/services/resources.service.ts
  39. 4
      ui-ngx/src/app/modules/common/modules-map.models.ts
  40. 68
      ui-ngx/src/app/modules/common/modules-map.ts
  41. 4
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html
  42. 10
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html
  43. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html
  44. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html
  45. 54
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html
  46. 37
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html
  47. 10
      ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html
  48. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html
  49. 6
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.html
  50. 4
      ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.html
  51. 4
      ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.html
  52. 36
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.html
  53. 4
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html
  54. 12
      ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.html
  55. 68
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
  56. 6
      ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.html
  57. 4
      ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html
  58. 10
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html
  59. 2
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-table.component.html
  60. 8
      ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html
  61. 15
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.html
  62. 153
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
  63. 10
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html
  64. 3
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-toolbar.component.html
  65. 20
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html
  66. 8
      ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html
  67. 4
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/add-new-breakpoint-dialog.component.html
  68. 12
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html
  69. 51
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.html
  70. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.scss
  71. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/move-widgets-dialog.component.html
  72. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.html
  73. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.html
  74. 9
      ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.html
  75. 23
      ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html
  76. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/widget-types-panel.component.html
  77. 8
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html
  78. 8
      ui-ngx/src/app/modules/home/components/dashboard/layout-button.scss
  79. 16
      ui-ngx/src/app/modules/home/components/dashboard/select-target-layout-dialog.component.html
  80. 6
      ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.html
  81. 16
      ui-ngx/src/app/modules/home/components/details-panel.component.html
  82. 2
      ui-ngx/src/app/modules/home/components/device/copy-device-credentials.component.html
  83. 4
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html
  84. 2
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  85. 6
      ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html
  86. 6
      ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.html
  87. 149
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  88. 14
      ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html
  89. 4
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.html
  90. 4
      ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.html
  91. 30
      ui-ngx/src/app/modules/home/components/entity/entity-filter.component.html
  92. 10
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html
  93. 8
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html
  94. 6
      ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html
  95. 6
      ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html
  96. 2
      ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.html
  97. 12
      ui-ngx/src/app/modules/home/components/filter/filter-dialog.component.html
  98. 42
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html
  99. 16
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html
  100. 6
      ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html

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

@ -279,7 +279,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1142,7 +1142,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1448,7 +1448,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1754,7 +1754,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -2060,7 +2060,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],

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

@ -279,7 +279,7 @@
"name": "Edit software",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1142,7 +1142,7 @@
"name": "Edit software",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1448,7 +1448,7 @@
"name": "Edit software",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1754,7 +1754,7 @@
"name": "Edit software",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -2060,7 +2060,7 @@
"name": "Edit software",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content class=\"flex flex-col\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],

4
application/src/main/data/json/demo/dashboards/thermostats.json

@ -160,7 +160,7 @@
"name": "Add",
"icon": "add",
"type": "customPretty",
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"add-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Add thermostat</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Thermostat name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n \n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n \n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"add-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Add thermostat</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content class=\"flex flex-col\">\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Thermostat name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" class=\"flex flex-col\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customCss": ".add-entity-form{\n width: 300px;\n}\n",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.addEntityFormGroup = vm.fb.group({\n entityName: ['', [vm.validators.required]],\n attributes: vm.fb.group({\n temperatureAlarmFlag: [false],\n temperatureAlarmThreshold: [{value: null, disabled: true}],\n humidityAlarmFlag: [false],\n humidityAlarmThreshold: [{value: null, disabled: true}]\n })\n });\n \n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n }\n });\n \n vm.addEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n }\n });\n\n vm.save = function() {\n vm.addEntityFormGroup.markAsPristine();\n saveEntityObservable().subscribe(\n function (entity) {\n saveAttributes(entity.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function saveEntityObservable() {\n const formValues = vm.addEntityFormGroup.value;\n let entity = {\n name: formValues.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.addEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if(attributes[key] !== null) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}",
"customResources": [],
@ -180,7 +180,7 @@
"name": "Edit",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Edit thermostat {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" readonly>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Edit thermostat {{entityName}}</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content class=\"flex flex-col\">\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" readonly>\n </mat-form-field>\n <div formGroupName=\"attributes\" class=\"flex flex-col\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customCss": ".edit-entity-form{\n width: 300px;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.attributes = {};\n \n vm.editEntityFormGroup = vm.fb.group({\n entityName: [''],\n attributes: vm.fb.group({\n temperatureAlarmFlag: [false],\n temperatureAlarmThreshold: [{value: null, disabled: true}],\n humidityAlarmFlag: [false],\n humidityAlarmThreshold: [{value: null, disabled: true}]\n })\n });\n \n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n }\n });\n \n vm.editEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n }\n });\n \n \n getEntityInfo();\n \n \n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveAttributes(entityId).subscribe(\n function () {\n vm.dialogRef.close(null);\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value;\n }\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n function (attributes) {\n getEntityAttributes(attributes);\n vm.editEntityFormGroup.patchValue({\n entityName: vm.entityName,\n attributes: vm.attributes\n });\n // if(vm.attributes.temperatureAlarmFlag) {\n // vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n // }\n // if(vm.attributes.humidityAlarmFlag) {\n // vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n // }\n }\n );\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.editEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if (attributes[key] !== vm.attributes[key]) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}",
"customResources": [],

2
application/src/main/data/json/system/widget_types/asset_admin_table.json

File diff suppressed because one or more lines are too long

4
application/src/main/data/json/system/widget_types/basic_gpio_control.json

File diff suppressed because one or more lines are too long

4
application/src/main/data/json/system/widget_types/basic_gpio_panel.json

@ -9,8 +9,8 @@
"sizeX": 5,
"sizeY": 2,
"resources": [],
"templateHtml": "<div class=\"gpio-panel\" style=\"height: 100%;\">\n <section fxLayout=\"row\" *ngFor=\"let row of rows\">\n <section fxFlex fxLayout=\"row\" *ngFor=\"let cell of row; let $index = index\">\n <section fxLayout=\"row\" fxFlex *ngIf=\"cell\" fxLayoutAlign=\"{{$index===0 ? 'end center' : 'start center'}}\">\n <span class=\"gpio-left-label\" [fxShow]=\"$index===0\">{{ cell.label }}</span>\n <section fxLayout=\"row\" class=\"led-panel\" [ngClass]=\"$index===0 ? 'col-0' : 'col-1'\"\n [ngStyle]=\"{backgroundColor: ledPanelBackgroundColor}\">\n <span class=\"pin\" [fxShow]=\"$index===0\">{{cell.pin}}</span>\n <span class=\"led-container\">\n <tb-led-light [size]=\"prefferedRowHeight\"\n [colorOn]=\"cell.colorOn\"\n [colorOff]=\"cell.colorOff\"\n [offOpacity]=\"'0.9'\"\n [enabled]=\"cell.enabled\">\n </tb-led-light>\n </span>\n <span class=\"pin\" [fxShow]=\"$index===1\">{{cell.pin}}</span>\n </section>\n <span class=\"gpio-right-label\" [fxShow]=\"$index===1\">{{ cell.label }}</span>\n </section>\n <section fxLayout=\"row\" fxFlex *ngIf=\"!cell\">\n <span fxFlex [fxShow]=\"$index===0\"></span>\n <span class=\"led-panel\"\n [ngStyle]=\"{height: prefferedRowHeight+'px', backgroundColor: ledPanelBackgroundColor}\"></span>\n <span fxFlex [fxShow]=\"$index===1\"></span>\n </section>\n </section>\n </section> \n</div>",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"templateHtml": "<div class=\"gpio-panel\" style=\"height: 100%;\">\n <section class=\"flex flex-row\" *ngFor=\"let row of rows\">\n <section class=\"flex flex-1 flex-row\" *ngFor=\"let cell of row; let $index = index\">\n <section class=\"flex flex-1 flex-row\" [class.justify-end]=\"$index===0\" [class.justify-start]=\"$index!==0\" *ngIf=\"cell\">\n <span class=\"gpio-left-label\" [class.!hidden]=\"$index!==0\">{{ cell.label }}</span>\n <section class=\"led-panel flex flex-row\" [class.col-0]=\"$index===0\" [class.col-1]=\"$index!==0\"\n [style.background-color]=\"ledPanelBackgroundColor\">\n <span class=\"pin\" [class.!hidden]=\"$index!==0\">{{cell.pin}}</span>\n <span class=\"led-container\">\n <tb-led-light [size]=\"prefferedRowHeight\"\n [colorOn]=\"cell.colorOn\"\n [colorOff]=\"cell.colorOff\"\n [offOpacity]=\"'0.9'\"\n [enabled]=\"cell.enabled\">\n </tb-led-light>\n </span>\n <span class=\"pin\" [class.!hidden]=\"$index!==1\">{{cell.pin}}</span>\n </section>\n <span class=\"gpio-right-label\" [class.!hidden]=\"$index!==1\">{{ cell.label }}</span>\n </section>\n <section class=\"flex flex-1 flex-row\" *ngIf=\"!cell\">\n <span class=\"flex-1\" [class.!hidden]=\"$index!==0\"></span>\n <span class=\"led-panel\"\n [style.height.px]=\"prefferedRowHeight\"\n [style.background-color]=\"ledPanelBackgroundColor\"></span>\n <span class=\"flex-1\" [class.!hidden]=\"$index!==1\"></span>\n </section>\n </section>\n </section>\n</div>",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section.flex-1 {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n\n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",

2
application/src/main/data/json/system/widget_types/device_admin_table.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/device_claiming_widget.json

@ -9,7 +9,7 @@
"sizeX": 7.5,
"sizeY": 4.5,
"resources": [],
"templateHtml": "<form *ngIf=\"claimDeviceFormGroup\" #claimDeviceForm=\"ngForm\" [formGroup]=\"claimDeviceFormGroup\"\n tb-toast toastTarget=\"{{ toastTargetId }}\"\n class=\"claim-form\" (ngSubmit)=\"claim(claimDeviceForm)\">\n <fieldset [disabled]=\"(isLoading$ | async) || loading\">\n <mat-form-field class=\"mat-block\">\n <mat-label *ngIf=\"showLabel\">{{deviceLabel}}</mat-label>\n <input matInput formControlName=\"deviceName\" required>\n <mat-error *ngIf=\"claimDeviceFormGroup.get('deviceName').hasError('required')\">\n {{requiredErrorDevice}}\n </mat-error>\n </mat-form-field>\n <mat-form-field *ngIf=\"secretKeyField\" class=\"mat-block\">\n <mat-label *ngIf=\"showLabel\">{{secretKeyLabel}}</mat-label>\n <input matInput formControlName=\"deviceSecret\" required>\n <mat-error *ngIf=\"claimDeviceFormGroup.get('deviceSecret').hasError('required')\">\n {{requiredErrorSecretKey}}\n </mat-error>\n </mat-form-field>\n </fieldset>\n <div class=\"mat-block\" fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\" [disabled]=\"(isLoading$ | async) || claimDeviceForm.invalid || !claimDeviceForm.dirty\">\n {{labelClaimButon}}\n </button>\n </div>\n</form>\n",
"templateHtml": "<form *ngIf=\"claimDeviceFormGroup\" #claimDeviceForm=\"ngForm\" [formGroup]=\"claimDeviceFormGroup\"\n tb-toast toastTarget=\"{{ toastTargetId }}\"\n class=\"claim-form\" (ngSubmit)=\"claim(claimDeviceForm)\">\n <fieldset [disabled]=\"(isLoading$ | async) || loading\">\n <mat-form-field class=\"mat-block\">\n <mat-label *ngIf=\"showLabel\">{{deviceLabel}}</mat-label>\n <input matInput formControlName=\"deviceName\" required>\n <mat-error *ngIf=\"claimDeviceFormGroup.get('deviceName').hasError('required')\">\n {{requiredErrorDevice}}\n </mat-error>\n </mat-form-field>\n <mat-form-field *ngIf=\"secretKeyField\" class=\"mat-block\">\n <mat-label *ngIf=\"showLabel\">{{secretKeyLabel}}</mat-label>\n <input matInput formControlName=\"deviceSecret\" required>\n <mat-error *ngIf=\"claimDeviceFormGroup.get('deviceSecret').hasError('required')\">\n {{requiredErrorSecretKey}}\n </mat-error>\n </mat-form-field>\n </fieldset>\n <div class=\"mat-block flex flex-row items-center justify-end\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\" [disabled]=\"(isLoading$ | async) || claimDeviceForm.invalid || !claimDeviceForm.dirty\">\n {{labelClaimButon}}\n </button>\n </div>\n</form>\n",
"templateCss": ".claim-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n",
"controllerScript": "let $scope;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n}\n\nfunction init() {\n $scope = self.ctx.$scope;\n let $injector = $scope.$injector;\n let utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n let $translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n let deviceService = $scope.$injector.get(self.ctx.servicesMap.get('deviceService'));\n let settings = self.ctx.settings || {};\n \n $scope.toastTargetId = 'device-claiming-widget' + utils.guid();\n $scope.secretKeyField = settings.deviceSecret;\n $scope.showLabel = settings.showLabel;\n\n let titleTemplate = \"\";\n let successfulClaim = utils.customTranslation(settings.successfulClaimDevice, settings.successfulClaimDevice) || $translate.instant('widgets.input-widgets.claim-successful');\n let failedClaimDevice = utils.customTranslation(settings.failedClaimDevice, settings.failedClaimDevice) || $translate.instant('widgets.input-widgets.claim-failed');\n let deviceNotFound = utils.customTranslation(settings.deviceNotFound, settings.deviceNotFound) || $translate.instant('widgets.input-widgets.claim-not-found');\n \n if (settings.widgetTitle && settings.widgetTitle.length) {\n titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n titleTemplate = self.ctx.widgetConfig.title;\n }\n self.ctx.widgetTitle = titleTemplate;\n \n $scope.deviceLabel = utils.customTranslation(settings.deviceLabel, settings.deviceLabel) || $translate.instant('widgets.input-widgets.device-name');\n $scope.requiredErrorDevice= utils.customTranslation(settings.requiredErrorDevice, settings.requiredErrorDevice) || $translate.instant('widgets.input-widgets.device-name-required');\n \n $scope.secretKeyLabel = utils.customTranslation(settings.secretKeyLabel, settings.secretKeyLabel) || $translate.instant('widgets.input-widgets.secret-key');\n $scope.requiredErrorSecretKey= utils.customTranslation(settings.requiredErrorSecretKey, settings.requiredErrorSecretKey) || $translate.instant('widgets.input-widgets.secret-key-required');\n \n $scope.labelClaimButon = utils.customTranslation(settings.labelClaimButon, settings.labelClaimButon) || $translate.instant('widgets.input-widgets.claim-device');\n \n $scope.claimDeviceFormGroup = $scope.fb.group(\n {deviceName: ['', [$scope.validators.required]]}\n );\n if ($scope.secretKeyField) {\n $scope.claimDeviceFormGroup.addControl('deviceSecret', $scope.fb.control('', [$scope.validators.required]));\n }\n \n $scope.claim = function(claimDeviceForm) {\n $scope.loading = true;\n\n let deviceName = $scope.claimDeviceFormGroup.get('deviceName').value;\n let claimRequest = {};\n if ($scope.secretKeyField) {\n claimRequest.secretKey = $scope.claimDeviceFormGroup.get('deviceSecret').value;\n }\n deviceService.claimDevice(deviceName, claimRequest, { ignoreErrors: true }).subscribe(\n function (data) {\n successClaim(claimDeviceForm);\n self.ctx.detectChanges();\n },\n function (error) {\n $scope.loading = false;\n if(error.status == 404) {\n $scope.showErrorToast(deviceNotFound, 'bottom', 'left', $scope.toastTargetId);\n } else {\n let errorMessage = failedClaimDevice;\n if (error.status !== 400) {\n if (error.error && error.error.message) {\n errorMessage = error.error.message;\n }\n }\n $scope.showErrorToast(errorMessage, 'bottom', 'left', $scope.toastTargetId);\n } \n self.ctx.detectChanges();\n }\n );\n }\n\n function successClaim(claimDeviceForm) {\n let deviceObj = {\n deviceName: ''\n };\n if ($scope.secretKeyField) {\n deviceObj.deviceSecret = '';\n } \n claimDeviceForm.resetForm(); \n $scope.claimDeviceFormGroup.reset(deviceObj);\n $scope.loading = false;\n $scope.showSuccessToast(successfulClaim, 2000, 'bottom', 'left', $scope.toastTargetId);\n self.ctx.updateAliases();\n }\n \n}\n",
"settingsSchema": "",

4
application/src/main/data/json/system/widget_types/raspberry_pi_gpio_control.json

File diff suppressed because one or more lines are too long

4
application/src/main/data/json/system/widget_types/raspberry_pi_gpio_panel.json

@ -9,8 +9,8 @@
"sizeX": 7,
"sizeY": 10.5,
"resources": [],
"templateHtml": "<div class=\"gpio-panel\" style=\"height: 100%;\">\n <section fxLayout=\"row\" *ngFor=\"let row of rows\">\n <section fxFlex fxLayout=\"row\" *ngFor=\"let cell of row; let $index = index\">\n <section fxLayout=\"row\" fxFlex *ngIf=\"cell\" fxLayoutAlign=\"{{$index===0 ? 'end center' : 'start center'}}\">\n <span class=\"gpio-left-label\" [fxShow]=\"$index===0\">{{ cell.label }}</span>\n <section fxLayout=\"row\" class=\"led-panel\" [ngClass]=\"$index===0 ? 'col-0' : 'col-1'\"\n [ngStyle]=\"{backgroundColor: ledPanelBackgroundColor}\">\n <span class=\"pin\" [fxShow]=\"$index===0\">{{cell.pin}}</span>\n <span class=\"led-container\">\n <tb-led-light [size]=\"prefferedRowHeight\"\n [colorOn]=\"cell.colorOn\"\n [colorOff]=\"cell.colorOff\"\n [offOpacity]=\"'0.9'\"\n [enabled]=\"cell.enabled\">\n </tb-led-light>\n </span>\n <span class=\"pin\" [fxShow]=\"$index===1\">{{cell.pin}}</span>\n </section>\n <span class=\"gpio-right-label\" [fxShow]=\"$index===1\">{{ cell.label }}</span>\n </section>\n <section fxLayout=\"row\" fxFlex *ngIf=\"!cell\">\n <span fxFlex [fxShow]=\"$index===0\"></span>\n <span class=\"led-panel\"\n [ngStyle]=\"{height: prefferedRowHeight+'px', backgroundColor: ledPanelBackgroundColor}\"></span>\n <span fxFlex [fxShow]=\"$index===1\"></span>\n </section>\n </section>\n </section> \n</div>",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"templateHtml": "<div class=\"gpio-panel\" style=\"height: 100%;\">\n <section class=\"flex flex-row\" *ngFor=\"let row of rows\">\n <section class=\"flex flex-1 flex-row\" *ngFor=\"let cell of row; let $index = index\">\n <section class=\"flex flex-1 flex-row\" [class.justify-end]=\"$index===0\" [class.justify-start]=\"$index!==0\" *ngIf=\"cell\">\n <span class=\"gpio-left-label\" [class.!hidden]=\"$index!==0\">{{ cell.label }}</span>\n <section class=\"led-panel flex flex-row\" [class.col-0]=\"$index===0\" [class.col-1]=\"$index!==0\"\n [style.background-color]=\"ledPanelBackgroundColor\">\n <span class=\"pin\" [class.!hidden]=\"$index!==0\">{{cell.pin}}</span>\n <span class=\"led-container\">\n <tb-led-light [size]=\"prefferedRowHeight\"\n [colorOn]=\"cell.colorOn\"\n [colorOff]=\"cell.colorOff\"\n [offOpacity]=\"'0.9'\"\n [enabled]=\"cell.enabled\">\n </tb-led-light>\n </span>\n <span class=\"pin\" [class.!hidden]=\"$index!==1\">{{cell.pin}}</span>\n </section>\n <span class=\"gpio-right-label\" [class.!hidden]=\"$index!==1\">{{ cell.label }}</span>\n </section>\n <section class=\"flex flex-1 flex-row\" *ngIf=\"!cell\">\n <span class=\"flex-1\" [class.!hidden]=\"$index!==0\"></span>\n <span class=\"led-panel\"\n [style.height.px]=\"prefferedRowHeight\"\n [style.background-color]=\"ledPanelBackgroundColor\"></span>\n <span class=\"flex-1\" [class.!hidden]=\"$index!==1\"></span>\n </section>\n </section>\n </section>\n</div>",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section.flex-1 {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n\n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",

2
application/src/main/data/json/system/widget_types/rpc_button.json

@ -9,7 +9,7 @@
"sizeX": 4,
"sizeY": 2,
"resources": [],
"templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\n <div fxFlex=\"20\" class=\"title-container\" fxLayout=\"row\"\n fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '10px'}\"\n class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-mdc-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [ngStyle]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\n fxLayout=\"row\" fxLayoutAlign=\"center center\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
"templateHtml": "<div class=\"tb-rpc-button flex flex-col\">\n <div class=\"title-container flex max-w-20% flex-full flex-row items-center justify-center\"\n [class.!hidden]=\"!showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div class=\"button-container flex flex-full flex-col items-center justify-center\"\n [class]=\"{\n 'max-w-80%': showTitle,\n 'max-w-100%': !showTitle\n }\"\n [style.padding-top]=\"showTitle ? '5px': '10px'\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-mdc-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [style]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container flex flex-row items-center justify-center\" [style.background]=\"error?.length ? 'rgba(255,255,255,0.25)' : 'none'\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
"templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
"controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n if (rpcParams.length) {\n try {\n rpcParams = JSON.parse(rpcParams);\n } catch (e) {}\n }\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n",
"settingsSchema": "",

2
application/src/main/data/json/system/widget_types/service_rpc.json

@ -10,7 +10,7 @@
"sizeY": 5.5,
"resources": [],
"templateHtml": "<tb-gateway-service-rpc [ctx]=\"ctx\"></tb-gateway-service-rpc>",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section.flex-1 {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n\n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}",
"controllerScript": "\nself.onInit = function() {\n};",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",

2
application/src/main/data/json/system/widget_types/update_boolean_timeseries.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_device_attribute.json

@ -9,7 +9,7 @@
"sizeX": 4,
"sizeY": 2,
"resources": [],
"templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\n <div fxFlex=\"20\" class=\"title-container\" fxLayout=\"row\"\n fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '10px'}\"\n class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <div>\n <button mat-button (click)=\"sendUpdate()\"\n [class.mat-mdc-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [ngStyle]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\n fxLayout=\"row\" fxLayoutAlign=\"center center\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
"templateHtml": "<div class=\"tb-rpc-button flex flex-col\">\n <div class=\"title-container flex max-w-20% flex-full flex-row items-center justify-center\"\n [class.!hidden]=\"!showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div class=\"button-container flex flex-full flex-col items-center justify-center\"\n [class]=\"{\n 'max-w-80%': showTitle,\n 'max-w-100%': !showTitle\n }\"\n [style.padding-top]=\"showTitle ? '5px': '10px'\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-mdc-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [style]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container flex flex-row items-center justify-center\" [style.background]=\"error?.length ? 'rgba(255,255,255,0.25)' : 'none'\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
"templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
"controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n let entityAttributeType = self.ctx.settings.entityAttributeType;\n let entityParameters = JSON.parse(self.ctx.settings.entityParameters);\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton\n .bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n\n self.ctx.$scope.sendUpdate = function() {\n let attributes = [];\n for (let key in entityParameters) {\n attributes.push({\n \"key\": key,\n \"value\": entityParameters[key]\n });\n }\n \n let entityId = {\n entityType: \"DEVICE\",\n id: self.ctx.defaultSubscription.targetDeviceId\n };\n attributeService.saveEntityAttributes(entityId,\n entityAttributeType, attributes).subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n\n );\n };\n}\n",
"settingsSchema": "",

2
application/src/main/data/json/system/widget_types/update_double_timeseries.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_integer_timeseries.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_location_timeseries.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_boolean_attribute.json

@ -9,7 +9,7 @@
"sizeX": 7.5,
"sizeY": 3,
"resources": [],
"templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n <form *ngIf=\"attributeUpdateFormGroup\"\r\n class=\"attribute-update-form\"\r\n [formGroup]=\"attributeUpdateFormGroup\"\r\n (ngSubmit)=\"updateAttribute()\">\r\n <div style=\"padding: 0 8px; margin: auto 0;\">\r\n <div class=\"attribute-update-form__grid\" [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\">\r\n <div class=\"grid__element\">\r\n <mat-checkbox formControlName=\"checkboxValue\"\r\n (change)=\"changed()\"\r\n aria-label=\"{{'widgets.input-widgets.switch-timeseries-value' | translate}}\">\r\n {{currentValue}}\r\n </mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [fxHide]=\"entityDetected\">\r\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [fxShow]=\"entityDetected && !isValidParameter\">\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n </div>\r\n </div>\r\n </form>\r\n</div>",
"templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n <form *ngIf=\"attributeUpdateFormGroup\"\r\n class=\"attribute-update-form\"\r\n [formGroup]=\"attributeUpdateFormGroup\"\r\n (ngSubmit)=\"updateAttribute()\">\r\n <div style=\"padding: 0 8px; margin: auto 0;\">\r\n <div class=\"attribute-update-form__grid\" [class.!hidden]=\"!entityDetected || !isValidParameter || !dataKeyDetected\">\r\n <div class=\"grid__element\">\r\n <mat-checkbox formControlName=\"checkboxValue\"\r\n (change)=\"changed()\"\r\n aria-label=\"{{'widgets.input-widgets.switch-timeseries-value' | translate}}\">\r\n {{currentValue}}\r\n </mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [class.!hidden]=\"entityDetected\">\r\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [class.!hidden]=\"!entityDetected || dataKeyDetected\">\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [class.!hidden]=\"!entityDetected || isValidParameter\">\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n </div>\r\n </div>\r\n </form>\r\n</div>",
"templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}",
"controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}",
"settingsSchema": "",

2
application/src/main/data/json/system/widget_types/update_server_date_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_double_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_image_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_integer_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_location_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_server_string_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_boolean_attribute.json

@ -9,7 +9,7 @@
"sizeX": 7.5,
"sizeY": 3,
"resources": [],
"templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n <form *ngIf=\"attributeUpdateFormGroup\"\r\n class=\"attribute-update-form\"\r\n [formGroup]=\"attributeUpdateFormGroup\"\r\n (ngSubmit)=\"updateAttribute()\">\r\n <div style=\"padding: 0 8px; margin: auto 0;\">\r\n <div class=\"attribute-update-form__grid\" [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\">\r\n <div class=\"grid__element\">\r\n <mat-checkbox formControlName=\"checkboxValue\"\r\n (change)=\"changed()\"\r\n aria-label=\"{{'widgets.input-widgets.switch-timeseries-value' | translate}}\">\r\n {{currentValue}}\r\n </mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [fxHide]=\"entityDetected\" [innerHtml]=\"message\"></div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [fxShow]=\"entityDetected && !isValidParameter\">\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n </div>\r\n </div>\r\n </form>\r\n</div>",
"templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n <form *ngIf=\"attributeUpdateFormGroup\"\r\n class=\"attribute-update-form\"\r\n [formGroup]=\"attributeUpdateFormGroup\"\r\n (ngSubmit)=\"updateAttribute()\">\r\n <div style=\"padding: 0 8px; margin: auto 0;\">\r\n <div class=\"attribute-update-form__grid\" [class.!hidden]=\"!entityDetected || !isValidParameter || !dataKeyDetected\">\r\n <div class=\"grid__element\">\r\n <mat-checkbox formControlName=\"checkboxValue\"\r\n (change)=\"changed()\"\r\n aria-label=\"{{'widgets.input-widgets.switch-timeseries-value' | translate}}\">\r\n {{currentValue}}\r\n </mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [class.!hidden]=\"entityDetected\" [innerHtml]=\"message\"></div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [class.!hidden]=\"!entityDetected || dataKeyDetected\">\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n </div>\r\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n [class.!hidden]=\"!entityDetected || isValidParameter\">\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n </div>\r\n </div>\r\n </form>\r\n</div>",
"templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}",
"controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue || false\n }\n ]\n ).subscribe(\n function success() {\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}",
"settingsSchema": "",

2
application/src/main/data/json/system/widget_types/update_shared_date_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_double_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_image_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_integer_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_location_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_string_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_string_timeseries.json

File diff suppressed because one or more lines are too long

12
application/src/main/data/json/tenant/dashboards/gateways.json

@ -186,7 +186,7 @@
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "customPretty",
"customHtml": "<div class=\"container\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>{{ 'gateway.launch-command' | translate }}</h2>\n <span fxFlex></span>\n <div [tb-help]=\"'gatewayInstall'\"></div>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <tb-gateway-command [deviceId]=\"entityId\"></tb-gateway-command>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n (click)=\"cancel()\" cdkFocusInitial>\n {{ 'action.close' | translate }}\n </button>\n </div>\n</div>\n",
"customHtml": "<div class=\"container\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>{{ 'gateway.launch-command' | translate }}</h2>\n <span class=\"flex-1\"></span>\n <div [tb-help]=\"'gatewayInstall'\"></div>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <tb-gateway-command [deviceId]=\"entityId\"></tb-gateway-command>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n (click)=\"cancel()\" cdkFocusInitial>\n {{ 'action.close' | translate }}\n </button>\n </div>\n</div>\n",
"customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n",
"customResources": [],
@ -246,7 +246,7 @@
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "customPretty",
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\r\n (ngSubmit)=\"save($event)\" class=\"add-entity-form\">\r\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\r\n <h2>Add gateway</h2>\r\n <span fxFlex></span>\r\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\r\n <mat-icon class=\"material-icons\">close</mat-icon>\r\n </button>\r\n </mat-toolbar>\r\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\r\n </mat-progress-bar>\r\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\r\n <div mat-dialog-content fxLayout=\"column\">\r\n <div fxLayout=\"row\" fxLayoutGap=\"8px\" fxLayout.xs=\"column\" fxLayoutGap.xs=\"0\">\r\n <mat-form-field fxFlex class=\"mat-block\">\r\n <mat-label>Name</mat-label>\r\n <input matInput formControlName=\"entityName\" required>\r\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\r\n Gateway name is required.\r\n </mat-error>\r\n </mat-form-field>\r\n </div>\r\n <div fxLayout=\"row\" fxLayoutGap=\"8px\" fxLayout.xs=\"column\" fxLayoutGap.xs=\"0\">\r\n <tb-entity-subtype-autocomplete\r\n fxFlex\r\n class=\"mat-block\"\r\n formControlName=\"type\"\r\n [required]=\"true\"\r\n [entityType]=\"'DEVICE'\"\r\n ></tb-entity-subtype-autocomplete>\r\n </div>\r\n </div>\r\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\r\n <button mat-button color=\"primary\"\r\n type=\"button\"\r\n [disabled]=\"(isLoading$ | async)\"\r\n (click)=\"cancel()\" cdkFocusInitial>\r\n Cancel\r\n </button>\r\n <button mat-button mat-raised-button color=\"primary\"\r\n type=\"submit\"\r\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\r\n Create\r\n </button>\r\n </div>\r\n</form>\r\n",
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save($event)\" class=\"add-entity-form\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>Add gateway</h2>\n <span class=\"flex-1\"></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content class=\"flex flex-col\">\n <div class=\"flex flex-row gap-2 xs:flex-col xs:gap-0\">\n <mat-form-field class=\"mat-block flex-1\">\n <mat-label>Name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Gateway name is required.\n </mat-error>\n </mat-form-field>\n </div>\n <div class=\"flex flex-row gap-2 xs:flex-col xs:gap-0\">\n <tb-entity-subtype-autocomplete\n class=\"mat-block flex-1\"\n formControlName=\"type\"\n [required]=\"true\"\n [entityType]=\"'DEVICE'\"\n ></tb-entity-subtype-autocomplete>\n </div>\n </div>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n </div>\n</form>\n",
"customCss": ".add-entity-form {\r\n min-width: 400px !important;\r\n}\r\n\r\n.add-entity-form .boolean-value-input {\r\n padding-left: 5px;\r\n}\r\n\r\n.add-entity-form .boolean-value-input .checkbox-label {\r\n margin-bottom: 8px;\r\n color: rgba(0,0,0,0.54);\r\n font-size: 12px;\r\n}\r\n\r\n.relations-list .header {\r\n padding-right: 5px;\r\n padding-bottom: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .header .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n font-size: 12px;\r\n font-weight: 700;\r\n color: rgba(0, 0, 0, .54);\r\n white-space: nowrap;\r\n}\r\n\r\n.relations-list .mat-form-field-infix {\r\n width: auto !important;\r\n}\r\n\r\n.relations-list .body {\r\n padding-right: 5px;\r\n padding-bottom: 15px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .row {\r\n padding-top: 5px;\r\n}\r\n\r\n.relations-list .body .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .md-button {\r\n margin: 0;\r\n}\r\n\r\n",
"customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Launch command\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n goToConfigState();\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n",
"customResources": [],
@ -592,7 +592,7 @@
"settings": {
"useMarkdownTextFunction": true,
"markdownTextPattern": "# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.",
"markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return `<mat-card-header class='tb-home-widget-link' (click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\">`\n } else {\n return \"<mat-card-header >\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n ${generateMatHeader(index)}\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors <span class='tb-hint' style='padding-left: 0'>(Enabled | Disabled)</span>\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;",
"markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return `<mat-card-header class='tb-home-widget-link' (click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\">`\n } else {\n return \"<mat-card-header >\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n ${generateMatHeader(index)}\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors <span class='tb-hint' style='padding-left: 0'>(Enabled | Disabled)</span>\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `<div class=\"cards-container flex flex-row flex-wrap gap-2\">${blockData}</div>`;",
"applyDefaultMarkdownStyle": false,
"markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}\n\n::ng-deep.tb-home-widget-link > div {\n flex-grow: 1;\n cursor: pointer;\n}\n\n .tb-home-widget-link {\n width: 100%;\n }\n\n .tb-home-widget-link:hover::after{\n color: inherit;\n }\n \n .tb-home-widget-link::after{\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px;\n}"
},
@ -1916,7 +1916,7 @@
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "customPretty",
"customHtml": "<div class=\"container\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>{{ 'gateway.launch-command' | translate }}</h2>\n <span fxFlex></span>\n <div [tb-help]=\"'gatewayInstall'\"></div>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <tb-gateway-command [deviceId]=\"entityId\"></tb-gateway-command>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n (click)=\"cancel()\" cdkFocusInitial>\n {{ 'action.close' | translate }}\n </button>\n </div>\n</div>\n",
"customHtml": "<div class=\"container\">\n <mat-toolbar class=\"flex flex-row\" color=\"primary\">\n <h2>{{ 'gateway.launch-command' | translate }}</h2>\n <span class=\"flex-1\"></span>\n <div [tb-help]=\"'gatewayInstall'\"></div>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <tb-gateway-command [deviceId]=\"entityId\"></tb-gateway-command>\n <div mat-dialog-actions class=\"flex flex-row items-center justify-end\">\n <button mat-button color=\"primary\"\n type=\"button\"\n (click)=\"cancel()\" cdkFocusInitial>\n {{ 'action.close' | translate }}\n </button>\n </div>\n</div>\n",
"customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n",
"customResources": [],
@ -1998,7 +1998,7 @@
"padding": "8px",
"settings": {
"useMarkdownTextFunction": true,
"markdownTextFunction": "let buttonsHtml = \"\" \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\n let disabled =false;\n if (index == 2) {\n disabled = data[0] && data[0].RemoteLoggingLevel ? data[0].RemoteLoggingLevel == \"NONE\" : true;\n } else if (index == 4) {\n const conf = data[0].general_configuration? JSON.parse(data[0].general_configuration): {};\n disabled = !conf.remoteShell;\n }\n buttonsHtml += `<button mat-raised-button disabled=${disabled} color=\"primary\"(click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\" fxFlex fxflex.md=\"50\">${btn.displayName}</button>`\n});\n\nreturn `<div class=\"action-buttons-container\" fxLayout=\"columnd\" fxLayout.md=\"row wrap\" fxLayoutGap=\"5px\" >${buttonsHtml}</div>`;",
"markdownTextFunction": "let buttonsHtml = \"\" \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\n let disabled =false;\n if (index == 2) {\n disabled = data[0] && data[0].RemoteLoggingLevel ? data[0].RemoteLoggingLevel == \"NONE\" : true;\n } else if (index == 4) {\n const conf = data[0].general_configuration? JSON.parse(data[0].general_configuration): {};\n disabled = !conf.remoteShell;\n }\n buttonsHtml += `<button mat-raised-button disabled=${disabled} color=\"primary\"(click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\" class=\"flex-1 md:max-w-50% md:flex-full\">${btn.displayName}</button>`\n});\n\nreturn `<div class=\"action-buttons-container flex flex-col gap-1.25 md:flex-row md:flex-wrap\">${buttonsHtml}</div>`;",
"applyDefaultMarkdownStyle": false,
"markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: start;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}"
},
@ -2605,7 +2605,7 @@
"padding": "0px",
"settings": {
"useMarkdownTextFunction": false,
"markdownTextPattern": "<div style=\"width: 100%; height: 100%; padding: 0;\" fxFlex fxLayout=\"column\">\n <mat-tab-group class=devices-tabs>\n <mat-tab label=\"All\" value=\"gateway_devices_0\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_0\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${mqttCount}\" label=\"MQTT\" value=\"gateway_devices_1\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_1\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${modbusCount}\" label=\"MODBUS\" value=\"gateway_devices_2\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_2\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${grpcCount}\" label=\"GRPC\" value=\"gateway_devices_3\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_3\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${opcuaCount}\" label=\"OPCUA\" value=\"gateway_devices_4\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_4\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bleCount}\" label=\"BLE\" value=\"gateway_devices_6\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_6\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${requestCount}\" label=\"REQUEST\" value=\"gateway_devices_7\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_7\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${canCount}\" label=\"CAN\" value=\"gateway_devices_8\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_8\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bacnetCount}\" label=\"BACNET\" value=\"gateway_devices_9\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_9\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${odbcCount}\" label=\"ODBC\" value=\"gateway_devices_10\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_10\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${restCount}\" label=\"REST\" value=\"gateway_devices_11\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_11\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${snmpCount}\" label=\"SNMP\" value=\"gateway_devices_12\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_12\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ftpCount}\" label=\"FTP\" value=\"gateway_devices_13\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_13\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${socketCount}\" label=\"SOCKET\" value=\"gateway_devices_14\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_14\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${xmppCount}\" label=\"XMPP\" value=\"gateway_devices_15\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_15\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ocppCount}\" label=\"OCPP\" value=\"gateway_devices_16\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_16\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${customCount}\" label=\"CUSTOM\" value=\"gateway_devices_17\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_17\"></tb-dashboard-state>\n </mat-tab>\n </mat-tab-group>\n</div>\n",
"markdownTextPattern": "<div style=\"padding: 0;\" class=\"flex size-full flex-1 flex-col\">\n <mat-tab-group class=devices-tabs>\n <mat-tab label=\"All\" value=\"gateway_devices_0\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_0\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${mqttCount}\" label=\"MQTT\" value=\"gateway_devices_1\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_1\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${modbusCount}\" label=\"MODBUS\" value=\"gateway_devices_2\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_2\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${grpcCount}\" label=\"GRPC\" value=\"gateway_devices_3\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_3\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${opcuaCount}\" label=\"OPCUA\" value=\"gateway_devices_4\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_4\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bleCount}\" label=\"BLE\" value=\"gateway_devices_6\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_6\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${requestCount}\" label=\"REQUEST\" value=\"gateway_devices_7\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_7\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${canCount}\" label=\"CAN\" value=\"gateway_devices_8\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_8\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bacnetCount}\" label=\"BACNET\" value=\"gateway_devices_9\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_9\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${odbcCount}\" label=\"ODBC\" value=\"gateway_devices_10\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_10\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${restCount}\" label=\"REST\" value=\"gateway_devices_11\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_11\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${snmpCount}\" label=\"SNMP\" value=\"gateway_devices_12\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_12\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ftpCount}\" label=\"FTP\" value=\"gateway_devices_13\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_13\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${socketCount}\" label=\"SOCKET\" value=\"gateway_devices_14\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_14\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${xmppCount}\" label=\"XMPP\" value=\"gateway_devices_15\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_15\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ocppCount}\" label=\"OCPP\" value=\"gateway_devices_16\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_16\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${customCount}\" label=\"CUSTOM\" value=\"gateway_devices_17\">\n <tb-dashboard-state [ctx]=\"ctx\" class=\"flex-1\" syncParentStateParams=\"true\" stateId=\"gateway_devices_17\"></tb-dashboard-state>\n </mat-tab>\n </mat-tab-group>\n</div>\n",
"applyDefaultMarkdownStyle": false,
"markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}\n\n.devices-tabs {\n height: 100%;\n}\n\n::ng-deep .mat-mdc-tab-body-wrapper {\n height: 100%;\n}"
},

9
ui-ngx/.eslintrc.json

@ -61,9 +61,14 @@
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
"plugin:@angular-eslint/template/recommended",
"plugin:tailwindcss/recommended"
],
"rules": {}
"rules": {
"tailwindcss/no-custom-classname": "off",
"tailwindcss/migration-from-tailwind-2": "off",
"tailwindcss/enforces-negative-arbitrary-values": "off"
}
}
]
}

4
ui-ngx/package.json

@ -147,16 +147,20 @@
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"@typescript-eslint/utils": "^8.7.0",
"autoprefixer": "^10.4.20",
"compression-webpack-plugin": "^11.1.0",
"directory-tree": "^3.5.2",
"eslint": "~8.57.1",
"eslint-plugin-import": "latest",
"eslint-plugin-jsdoc": "latest",
"eslint-plugin-prefer-arrow": "latest",
"eslint-plugin-tailwindcss": "^3.17.5",
"ngrx-store-freeze": "^0.2.4",
"patch-package": "^8.0.0",
"postcss": "^8.4.47",
"postinstall-prepare": "^2.0.0",
"raw-loader": "^4.0.2",
"tailwindcss": "^3.4.13",
"ts-node": "^10.9.2",
"typescript": "~5.5.4",
"webpack": "5.95.0"

2
ui-ngx/src/app/core/core.module.ts

@ -35,7 +35,6 @@ import { TbMissingTranslationHandler } from './translate/missing-translate-handl
import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DEFAULT_OPTIONS, MatDialogConfig, MatDialogModule } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { FlexLayoutModule } from '@angular/flex-layout';
import { TranslateDefaultCompiler } from '@core/translate/translate-default-compiler';
import { WINDOW_PROVIDERS } from '@core/services/window.service';
import { HotkeyModule } from 'angular2-hotkeys';
@ -44,7 +43,6 @@ import { TranslateDefaultLoader } from '@core/translate/translate-default-loader
import { EntityConflictInterceptor } from '@core/interceptors/entity-conflict.interceptor';
@NgModule({ exports: [], imports: [CommonModule,
FlexLayoutModule.withConfig({ addFlexToParent: false }),
MatDialogModule,
MatButtonModule,
MatSnackBarModule,

9
ui-ngx/src/app/core/services/dynamic-component-factory.service.ts

@ -15,10 +15,11 @@
///
import { Component, Injectable, Type, ɵComponentDef, ɵNG_COMP_DEF } from '@angular/core';
import { from, Observable, of } from 'rxjs';
import { forkJoin, from, Observable, of } from 'rxjs';
import { CommonModule } from '@angular/common';
import { mergeMap } from 'rxjs/operators';
import { guid } from '@core/utils';
import { getFlexLayoutModule } from '@shared/legacy/flex-layout.models';
@Injectable({
providedIn: 'root'
@ -34,9 +35,9 @@ export class DynamicComponentFactoryService {
imports?: Type<any>[],
preserveWhitespaces?: boolean,
styles?: string[]): Observable<Type<T>> {
return from(import('@angular/compiler')).pipe(
mergeMap(() => {
let componentImports: Type<any>[] = [CommonModule];
return forkJoin({flexLayoutModule: getFlexLayoutModule(), compiler: from(import('@angular/compiler'))}).pipe(
mergeMap((data) => {
let componentImports: Type<any>[] = [CommonModule, data.flexLayoutModule];
if (imports) {
componentImports = [...componentImports, ...imports];
}

77
ui-ngx/src/app/core/services/resources.service.ts

@ -27,7 +27,7 @@ import {
ɵNgModuleDef
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Observable, ReplaySubject, throwError } from 'rxjs';
import { forkJoin, from, Observable, ReplaySubject, throwError } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { IModulesMap } from '@modules/common/modules-map.models';
import { TbResourceId } from '@shared/models/id/tb-resource-id';
@ -38,6 +38,7 @@ import { selectIsAuthenticated } from '@core/auth/auth.selectors';
import { AppState } from '@core/core.state';
import { map, tap } from 'rxjs/operators';
import { RequestConfig } from '@core/http/http-utils';
import { getFlexLayoutModule } from '@app/shared/legacy/flex-layout.models';
export interface ModuleInfo {
module: ɵNgModuleDef<any>;
@ -187,26 +188,46 @@ export class ResourcesService {
if (this.loadedModulesWithComponents[url]) {
return this.loadedModulesWithComponents[url].asObservable();
}
modulesMap.init();
const meta = this.getMetaInfo(resourceId);
const subject = new ReplaySubject<ModulesWithComponents>();
this.loadedModulesWithComponents[url] = subject;
import('@angular/compiler').then(
forkJoin(
[
modulesMap.init(),
from(import('@angular/compiler'))
]
).subscribe(
() => {
// @ts-ignore
System.import(url, undefined, meta).then(
(module: any) => {
try {
const modulesWithComponents = this.extractModulesWithComponents(module);
if (modulesWithComponents.modules.length || modulesWithComponents.standaloneComponents.length) {
for (const module of modulesWithComponents.modules) {
createNgModule(module.module.type, this.injector);
this.patchModulesWithFlexLayout(modulesWithComponents).subscribe(
{
next: modules => {
if (modules.modules.length || modules.standaloneComponents.length) {
try {
for (const module of modules.modules) {
createNgModule(module.module.type, this.injector);
}
this.loadedModulesWithComponents[url].next(modulesWithComponents);
this.loadedModulesWithComponents[url].complete();
} catch (e) {
console.log(`Unable to parse module from url: ${url}`, e);
this.loadedModulesWithComponents[url].error(new Error(`Unable to parse module from url: ${url}`));
}
} else {
this.loadedModulesWithComponents[url].error(new Error(`Module '${url}' doesn't have exported modules or components!`));
}
},
error: err => {
console.log(`Unable to patch module with flexLayout, module url: ${url}`, err);
this.loadedModulesWithComponents[url].error(new Error(`Unable to patch module with flexLayout, module url: ${url}`));
}
}
this.loadedModulesWithComponents[url].next(modulesWithComponents);
this.loadedModulesWithComponents[url].complete();
} else {
this.loadedModulesWithComponents[url].error(new Error(`Module '${url}' doesn't have exported modules or components!`));
}
);
} catch (e) {
console.log(`Unable to parse module from url: ${url}`, e);
this.loadedModulesWithComponents[url].error(new Error(`Unable to parse module from url: ${url}`));
@ -284,6 +305,40 @@ export class ResourcesService {
return modulesWithComponents;
}
private patchModulesWithFlexLayout(modulesWithComponents: ModulesWithComponents): Observable<ModulesWithComponents> {
return getFlexLayoutModule().pipe(
map((flexLayoutModule) => {
modulesWithComponents.modules.forEach(m => {
if (Array.isArray(m.module.imports)) {
if (!m.module.imports.includes(flexLayoutModule)) {
m.module.imports.push(flexLayoutModule);
}
} else {
const imports = m.module.imports();
if (!imports.includes(flexLayoutModule)) {
imports.push(flexLayoutModule);
m.module.imports = imports;
}
}
});
modulesWithComponents.standaloneComponents.forEach(c => {
if (Array.isArray(c.dependencies)) {
if (!c.dependencies.includes(flexLayoutModule)) {
c.dependencies.push(flexLayoutModule);
}
} else {
const dependencies = c.dependencies();
if (!dependencies.includes(flexLayoutModule)) {
dependencies.push(flexLayoutModule);
c.dependencies = dependencies;
}
}
});
return modulesWithComponents;
})
);
}
private loadResourceByType(type: 'css' | 'js', url: string): Observable<any> {
const subject = new ReplaySubject<void>();
this.loadedResources[url] = subject;

4
ui-ngx/src/app/modules/common/modules-map.models.ts

@ -14,6 +14,8 @@
/// limitations under the License.
///
import { Observable } from 'rxjs';
export interface IModulesMap {
init(): void;
init(): Observable<any>;
}

68
ui-ngx/src/app/modules/common/modules-map.ts

@ -20,10 +20,6 @@ import * as AngularAnimations from '@angular/animations';
import * as AngularCore from '@angular/core';
import * as AngularCommon from '@angular/common';
import * as AngularForms from '@angular/forms';
import * as AngularFlexLayout from '@angular/flex-layout';
import * as AngularFlexLayoutFlex from '@angular/flex-layout/flex';
import * as AngularFlexLayoutGrid from '@angular/flex-layout/grid';
import * as AngularFlexLayoutExtended from '@angular/flex-layout/extended';
import * as AngularPlatformBrowser from '@angular/platform-browser';
import * as AngularPlatformBrowserAnimations from '@angular/platform-browser/animations';
import * as AngularRouter from '@angular/router';
@ -338,6 +334,8 @@ import { IModulesMap } from '@modules/common/modules-map.models';
import { TimezoneComponent } from '@shared/components/time/timezone.component';
import { TimezonePanelComponent } from '@shared/components/time/timezone-panel.component';
import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component';
import { Observable, map, of } from 'rxjs';
import { getFlexLayout } from '@shared/legacy/flex-layout.models';
class ModulesMap implements IModulesMap {
@ -349,10 +347,10 @@ class ModulesMap implements IModulesMap {
'@angular/common': AngularCommon,
'@angular/common/http': HttpClientModule,
'@angular/forms': AngularForms,
'@angular/flex-layout': AngularFlexLayout,
'@angular/flex-layout/flex': AngularFlexLayoutFlex,
'@angular/flex-layout/grid': AngularFlexLayoutGrid,
'@angular/flex-layout/extended': AngularFlexLayoutExtended,
'@angular/flex-layout': {},
'@angular/flex-layout/flex': {},
'@angular/flex-layout/grid': {},
'@angular/flex-layout/extended': {},
'@angular/platform-browser': AngularPlatformBrowser,
'@angular/platform-browser/animations': AngularPlatformBrowserAnimations,
'@angular/router': AngularRouter,
@ -669,30 +667,40 @@ class ModulesMap implements IModulesMap {
'@home/components/queue/queue-form.component': QueueFormComponent
};
init() {
init(): Observable<any> {
if (!this.initialized) {
System.constructor.prototype.resolve = (id: string) => {
try {
if (this.modulesMap[id]) {
return 'app:' + id;
} else {
return id;
return getFlexLayout().pipe(
map((flexLayout) => {
this.modulesMap['@angular/flex-layout'] = flexLayout;
this.modulesMap['@angular/flex-layout/flex'] = flexLayout;
this.modulesMap['@angular/flex-layout/grid'] = flexLayout;
this.modulesMap['@angular/flex-layout/extended'] = flexLayout;
System.constructor.prototype.resolve = (id: string) => {
try {
if (this.modulesMap[id]) {
return 'app:' + id;
} else {
return id;
}
} catch (err) {
return id;
}
};
for (const moduleId of Object.keys(this.modulesMap)) {
System.set('app:' + moduleId, this.modulesMap[moduleId]);
}
} catch (err) {
return id;
}
};
for (const moduleId of Object.keys(this.modulesMap)) {
System.set('app:' + moduleId, this.modulesMap[moduleId]);
}
System.constructor.prototype.shouldFetch = (url: string) => url.endsWith('/download');
System.constructor.prototype.fetch = (url: string, options: RequestInit & {meta?: any}) => {
if (options?.meta?.additionalHeaders) {
options.headers = { ...options.headers, ...options.meta.additionalHeaders };
}
return fetch(url, options);
};
this.initialized = true;
System.constructor.prototype.shouldFetch = (url: string) => url.endsWith('/download');
System.constructor.prototype.fetch = (url: string, options: RequestInit & {meta?: any}) => {
if (options?.meta?.additionalHeaders) {
options.headers = { ...options.headers, ...options.meta.additionalHeaders };
}
return fetch(url, options);
};
this.initialized = true;
})
);
} else {
return of(null);
}
}
}

4
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html

@ -35,11 +35,11 @@
<span>{{ assignedToCurrentUserText | translate }}</span>
</mat-option>
<mat-option *ngFor="let user of filteredUsers | async" [value]="user"
[fxHide]="assigneeId === user.id.id">
[class.!hidden]="assigneeId === user.id.id">
<span class="user-avatar" [innerHTML]="getUserInitials(user)"
[style.background-color]="getAvatarBgColor(user)">
</span>
<div class="user-display-name" fxLayout="column" fxLayoutGap="2px">
<div class="user-display-name flex flex-col gap-0.5">
<span *ngIf="user.firstName || user.lastName"
[innerHTML]="getFullName(user) | highlight:searchText"></span>
<span [innerHTML]="user.email | highlight:searchText"></span>

10
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html

@ -16,16 +16,16 @@
-->
<ng-container [formGroup]="assigneeFormGroup">
<mat-form-field [ngStyle]="inline ? {width: '100%'} : {marginBottom: '25px'}"
<mat-form-field [style]="inline ? {width: '100%'} : {marginBottom: '25px'}"
(click)="openAlarmAssigneeSelectPanel($event)"
subscriptSizing="dynamic" [appearance]="inline ? 'outline' : 'fill'">
<mat-label *ngIf="!inline" translate>alarm.assignee</mat-label>
<input matInput readonly formControlName="assignee">
<span *ngIf="assignee" matPrefix class="user-avatar" [ngClass]="{'inline': inline}"
[style.backgroundColor]="getAvatarBgColor()">
<span *ngIf="assignee" matPrefix class="user-avatar" [class.inline]="inline"
[style.background-color]="getAvatarBgColor()">
{{ getUserInitials() }}
</span>
<mat-icon *ngIf="!assignee" matPrefix class="unassigned-icon" [ngClass]="{'inline': inline}">account_circle</mat-icon>
<mat-icon *ngIf="!disabled" matSuffix class="drop-down-icon" [ngClass]="{'inline': inline}">arrow_drop_down</mat-icon>
<mat-icon *ngIf="!assignee" matPrefix class="unassigned-icon" [class.inline]="inline">account_circle</mat-icon>
<mat-icon *ngIf="!disabled" matSuffix class="drop-down-icon" [class.inline]="inline">arrow_drop_down</mat-icon>
</mat-form-field>
</ng-container>

2
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<mat-form-field fxFlex class="mat-block" style="margin-bottom: 25px"
<mat-form-field class="mat-block flex-1" style="margin-bottom: 25px"
(click)="openAlarmAssigneePanel($event, alarm)"
subscriptSizing="dynamic">
<mat-label translate>alarm.assignee</mat-label>

2
ui-ngx/src/app/modules/home/components/alarm/alarm-comment-dialog.component.html

@ -17,7 +17,7 @@
-->
<form style="width: 600px;">
<mat-toolbar color="primary">
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button (click)="close()" type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>

54
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html

@ -15,10 +15,11 @@
limitations under the License.
-->
<div [formGroup]="alarmCommentFormGroup" class="tb-alarm-comments" fxLayout="column">
<div class="header" [ngClass]="{'activity-only': alarmActivityOnly}">
<div class="header-container" fxLayout="row" fxLayoutAlign="space-between center"
[ngClass]="{'asc': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<div [formGroup]="alarmCommentFormGroup" class="tb-alarm-comments flex flex-col">
<div class="header" [class.activity-only]="alarmActivityOnly">
<div class="header-container flex flex-row items-center justify-between"
[class.asc]="isDirectionAscending()"
[class.activity-only]="alarmActivityOnly">
<span class="header-title" translate>
alarm-activity.activity
</span>
@ -47,13 +48,14 @@
</div>
</div>
</div>
<div fxLayout="column">
<div class="flex flex-col">
<ng-container *ngIf="!isDirectionAscending()">
<ng-container *ngTemplateOutlet="commentInput"></ng-container>
</ng-container>
<div class="comments-container" fxLayout="column" fxLayoutGap="12px" style="padding: 24px"
[ngClass]="{'asc': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<div fxFlex *ngFor="let displayDataElement of displayData; index as i">
<div class="comments-container flex flex-col gap-3" style="padding: 24px"
[class.asc]="isDirectionAscending()"
[class.activity-only]="alarmActivityOnly">
<div class="flex-1" *ngFor="let displayDataElement of displayData; index as i">
<div class="system-comment" *ngIf="displayDataElement.isSystemComment; else userComment">
<span class="system-text" style="margin-right: 8px">
{{ displayDataElement.commentText }}
@ -65,20 +67,20 @@
</span>
</div>
<ng-template #userComment>
<div class="user-comment" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"
<div class="user-comment flex flex-row items-center justify-start gap-2"
*ngIf="!displayDataElement.edit; else commentEditing"
(mouseenter)="onCommentMouseEnter(displayDataElement.commentId, i)"
(mouseleave)="onCommentMouseLeave(i)">
<div *ngIf="displayDataElement.userExists; else userDeleted" class="user-avatar"
fxLayout="row" fxLayoutAlign="center center" fxFlexAlign="start" fxHide.xs
<div *ngIf="displayDataElement.userExists; else userDeleted"
class="user-avatar flex flex-row items-center justify-center self-start xs:!hidden"
[style.background-color]="displayDataElement.avatarBgColor">
{{ getUserInitials(displayDataElement.displayName) }}
</div>
<ng-template #userDeleted>
<mat-icon class="mat-icon tb-mat-28" fxHide.xs>no_accounts</mat-icon>
<mat-icon class="mat-icon tb-mat-28 xs:!hidden">no_accounts</mat-icon>
</ng-template>
<div fxFlex fxLayout="column" fxLayoutGap="5px">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<div class="flex flex-1 flex-col" style="gap: 5px">
<div class="flex flex-row items-center justify-start gap-2">
<span class="user-name">{{ displayDataElement.displayName }}</span>
<span class="time"
[matTooltip]="displayDataElement.createdTime"
@ -93,8 +95,8 @@
</div>
<span class="text">{{ displayDataElement.commentText }}</span>
</div>
<div fxLayout="row" fxLayout.xs="column" class="action-buttons"
[ngClass]="{ 'show-buttons': displayDataElement.showActions }">
<div class="action-buttons flex flex-row xs:flex-col"
[class.show-buttons]="displayDataElement.showActions">
<button mat-icon-button
type="button"
(click)="editComment(displayDataElement.commentId)">
@ -108,13 +110,12 @@
</div>
</div>
<ng-template #commentEditing>
<div fxLayoutAlign="row center" fxLayoutGap="8px">
<div fxLayout="row" fxLayoutAlign="center center"
class="user-avatar"
<div class="flex flex-row items-center justify-start gap-2">
<div class="user-avatar flex flex-row items-center justify-center"
[style.background-color]="displayDataElement.avatarBgColor">
{{ getUserInitials(displayDataElement.displayName) }}
</div>
<mat-form-field fxFlex class="mat-block tb-appearance-transparent">
<mat-form-field class="mat-block tb-appearance-transparent flex-1">
<textarea matInput
type="text"
placeholder="{{ 'alarm-activity.add' | translate }}"
@ -125,7 +126,7 @@
(keyup.enter)="saveEditedComment(displayDataElement.commentId)"
(keydown.enter)="$event.preventDefault()">
</textarea>
<div matSuffix fxLayout="row">
<div matSuffix class="flex flex-row">
<button mat-icon-button
(click)="cancelEdit(displayDataElement.commentId)"
type="button">
@ -149,15 +150,16 @@
</div>
<ng-template #commentInput>
<div style="background-color: white" class="comment-input"
[ngClass]="{'newest-first': !isDirectionAscending(), 'oldest-first': isDirectionAscending(), 'activity-only': alarmActivityOnly}">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" class="inner-wrap">
<div fxLayout="row" fxLayoutAlign="center center"
class="user-avatar"
[class.newest-first]="!isDirectionAscending()"
[class.oldest-first]="isDirectionAscending()"
[class.activity-only]="alarmActivityOnly">
<div class="inner-wrap flex flex-row items-center justify-start gap-2">
<div class="user-avatar flex flex-row items-center justify-center"
*ngIf="userDisplayName$ | async; let userDisplayName"
[style.background-color]="getCurrentUserBgColor(userDisplayName)">
{{ getUserInitials(userDisplayName) }}
</div>
<mat-form-field fxFlex class="mat-block tb-appearance-transparent">
<mat-form-field class="mat-block tb-appearance-transparent flex-1">
<textarea matInput
type="text"
placeholder="{{ 'alarm-activity.add' | translate }}"

37
ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html

@ -18,7 +18,7 @@
<form [formGroup]="alarmFormGroup" style="width: 600px;">
<mat-toolbar color="primary">
<h2>{{ 'alarm.alarm-details' | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="close()"
type="button">
@ -27,45 +27,46 @@
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div style="height: 4px;" *ngIf="(isLoading$ | async) === false"></div>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" style="margin-bottom: 22px">
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field fxFlex class="mat-block">
<div class="flex flex-row gap-1.5">
<mat-form-field class="mat-block flex-1">
<mat-label translate>alarm.originator</mat-label>
<input matInput formControlName="originatorName" readonly>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-form-field class="mat-block flex-1">
<mat-label translate>alarm.severity</mat-label>
<input matInput formControlName="alarmSeverity" readonly
[ngStyle]="{fontWeight: 'bold', color: alarmSeverityColorsMap.get((alarm$ | async)?.severity)}">
style="font-weight: bold"
[style.color]="alarmSeverityColorsMap.get((alarm$ | async)?.severity)">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field *ngIf="alarmFormGroup.get('startTime').value" fxFlex class="mat-block">
<div class="flex flex-row gap-1.5">
<mat-form-field *ngIf="alarmFormGroup.get('startTime').value" class="mat-block flex-1">
<mat-label translate>alarm.start-time</mat-label>
<input matInput formControlName="startTime" readonly>
</mat-form-field>
<mat-form-field *ngIf="alarmFormGroup.get('duration').value" fxFlex class="mat-block">
<mat-form-field *ngIf="alarmFormGroup.get('duration').value" class="mat-block flex-1">
<mat-label translate>alarm.duration</mat-label>
<input matInput formControlName="duration" readonly>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="6px">
<mat-form-field fxFlex class="mat-block">
<div class="flex flex-row gap-1.5">
<mat-form-field class="mat-block flex-1">
<mat-label translate>alarm.type</mat-label>
<input matInput formControlName="type" readonly>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-form-field class="mat-block flex-1">
<mat-label translate>alarm.status</mat-label>
<input matInput formControlName="alarmStatus" readonly>
</mat-form-field>
</div>
<mat-expansion-panel class="tb-alarm-details">
<mat-expansion-panel-header fxLayout="row wrap" style="margin-bottom: 8px">
<mat-expansion-panel-header class="flex flex-row flex-wrap" style="margin-bottom: 8px">
<mat-panel-title>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" translate>
<mat-panel-description class="flex items-center justify-end" translate>
alarm.show-more
</mat-panel-description>
</mat-expansion-panel-header>
@ -79,15 +80,15 @@
</ng-template>
</mat-expansion-panel>
</fieldset>
<tb-alarm-assignee fxFlex [allowAssign]="allowAssign"
<tb-alarm-assignee class="flex-1" [allowAssign]="allowAssign"
[alarm]="alarm$ | async"
(alarmReassigned)="onReassign()">
</tb-alarm-assignee>
<tb-alarm-comment #alarmCommentComponent [alarmId]="alarmId"></tb-alarm-comment>
</div>
<div mat-dialog-actions fxLayout="row">
<span fxFlex></span>
<div fxLayout="row" *ngIf="alarm$ | async; let alarm;" fxLayoutGap="8px">
<div mat-dialog-actions class="flex flex-row">
<span class="flex-1"></span>
<div class="flex flex-row gap-2" *ngIf="alarm$ | async; let alarm;">
<button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK ||
alarm.status === alarmStatuses.CLEARED_UNACK)"
mat-raised-button

10
ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html

@ -30,16 +30,16 @@
</ng-container>
</ng-template>
<ng-template #alarmFilterPanel>
<form fxLayout="column" class="mat-content mat-padding" (ngSubmit)="update()">
<form class="mat-content mat-padding flex flex-col" (ngSubmit)="update()">
<ng-container *ngTemplateOutlet="alarmFilter"></ng-container>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<div class="tb-panel-actions flex flex-row items-center justify-end">
<button type="button"
mat-button
color="primary"
(click)="reset()">
{{ 'action.reset' | translate }}
</button>
<span fxFlex></span>
<span class="flex-1"></span>
<button type="button"
mat-button
(click)="cancel()">
@ -73,7 +73,7 @@
</mat-chip-listbox>
</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width" ngClass.xs="filters-title-mobile" translate>alarm.alarm-type-list</div>
<div class="fixed-title-width" ranslate>alarm.alarm-type-list</div>
<tb-entity-subtype-list subscriptSizing="dynamic"
formControlName="typeList"
appearance="outline"
@ -83,7 +83,7 @@
</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width" translate>alarm.assignee</div>
<tb-alarm-assignee-select fxFlex inline="true"
<tb-alarm-assignee-select class="flex-1" inline="true"
[userMode]="userMode"
formControlName="assigneeId">
</tb-alarm-assignee-select>

2
ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html

@ -15,4 +15,4 @@
limitations under the License.
-->
<tb-entities-table [entitiesTableConfig]="alarmTableConfig" [ngClass]="{'tb-details-mode': detailsMode}"></tb-entities-table>
<tb-entities-table [entitiesTableConfig]="alarmTableConfig" [class.tb-details-mode]="detailsMode"></tb-entities-table>

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

@ -15,9 +15,9 @@
limitations under the License.
-->
<div fxLayout="column" class="mat-content mat-padding">
<div fxLayout="row" *ngFor="let alias of entityAliasesInfo | keyvalue">
<tb-aliases-entity-autocomplete fxFlex
<div class="mat-content mat-padding flex flex-col">
<div class="flex flex-row" *ngFor="let alias of entityAliasesInfo | keyvalue">
<tb-aliases-entity-autocomplete class="flex-1"
required
[alias]="alias.value.alias"
[entityFilter]="alias.value.entityFilter"

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

@ -15,7 +15,7 @@
limitations under the License.
-->
<section class="tb-aliases-entity-select" fxLayout="row" fxLayoutAlign="start center" *ngIf="hasSelectableAliasEntities">
<section class="tb-aliases-entity-select flex flex-row items-center justify-start" *ngIf="hasSelectableAliasEntities">
<button mat-icon-button
cdkOverlayOrigin #aliasEntitySelectPanelOrigin="cdkOverlayOrigin"
(click)="openEditMode()"
@ -23,7 +23,7 @@
[matTooltipPosition]="tooltipPosition">
<mat-icon class="material-icons">devices_other</mat-icon>
</button>
<span fxHide.lt-lg
<span class="lt-lg:!hidden"
(click)="openEditMode()"
matTooltip="{{ 'entity.select-entities' | translate }}"
[matTooltipPosition]="tooltipPosition">

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

@ -18,7 +18,7 @@
<form [formGroup]="entityAliasFormGroup" (ngSubmit)="save()" style="width: 700px;">
<mat-toolbar color="primary">
<h2>{{ (isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -55,7 +55,7 @@
? translate.instant('alias.entity-filter-no-entity-matched') : ''"></tb-error>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

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

@ -18,7 +18,7 @@
<form [formGroup]="entityAliasesFormGroup" (ngSubmit)="save()" style="width: 800px;">
<mat-toolbar color="primary">
<h2>{{ title | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -27,36 +27,34 @@
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div style="height: 4px;" *ngIf="(isLoading$ | async) === false"></div>
<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 fxFlex.gt-sm="200px">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>
<div class="tb-aliases-header flex flex-row items-center justify-start">
<span class="max-w-5% flex-full"></span>
<div class="flex max-w-95% flex-full flex-row items-center justify-start">
<span class="tb-header-label flex-1 gt-sm:min-w-52 gt-sm:max-w-52 gt-sm:basis-52" translate>alias.name</span>
<span class="tb-header-label flex-1" translate style="padding-left: 10px;">alias.entity-filter</span>
<span class="tb-header-label min-w-32 max-w-32 flex-1 basis-32" translate style="padding-left: 10px;">alias.resolve-multiple</span>
<span style="min-width: 80px;"></span>
</div>
</div>
<fieldset [disabled]="isLoading$ | async">
<mat-divider></mat-divider>
<div fxFlex fxLayout="row" fxLayoutAlign="start center"
<div class="flex flex-1 flex-row items-center justify-start"
formArrayName="entityAliases"
*ngFor="let entityAliasControl of entityAliasesFormArray().controls; let $index = index">
<span fxFlex="5">{{$index + 1}}.</span>
<div class="mat-elevation-z4 tb-alias" fxFlex="95" fxLayout="row" fxLayoutAlign="start center">
<mat-form-field class="mat-block" fxFlex fxFlex.gt-sm="200px" style="padding-top: 20px;">
<span class="max-w-5% flex-full">{{$index + 1}}.</span>
<div class="mat-elevation-z4 tb-alias flex max-w-95% flex-full flex-row items-center justify-start">
<mat-form-field class="mat-block flex-1 gt-sm:min-w-52 gt-sm:max-w-52 gt-sm:basis-52" style="padding-top: 20px;">
<input matInput [formControl]="entityAliasControl.get('alias')" required placeholder="{{ 'entity.alias' | translate }}">
<mat-error *ngIf="entityAliasControl.get('alias').hasError('required')">
{{ 'entity.alias-required' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-filter-view fxFlex style="padding-left: 10px;" [formControl]="entityAliasControl.get('filter')">
<tb-entity-filter-view class="flex-1" style="padding-left: 10px;" [formControl]="entityAliasControl.get('filter')">
</tb-entity-filter-view>
<section fxFlex="120px" style="padding-left: 10px;"
class="tb-resolve-multiple-switch"
fxLayout="column"
fxLayoutAlign="center center">
<section style="padding-left: 10px;"
class="tb-resolve-multiple-switch flex min-w-32 max-w-32 flex-1 basis-32 flex-col items-center justify-center">
<mat-slide-toggle class="resolve-multiple-switch"
[formControl]="entityAliasControl.get('resolveMultiple')">
</mat-slide-toggle>
@ -87,13 +85,13 @@
<button mat-raised-button color="primary"
type="button"
(click)="addAlias()"
[fxShow]="!disableAdd"
[class.!hidden]="disableAdd"
[disabled]="isLoading$ | async"
matTooltip="{{ 'alias.add' | translate }}"
matTooltipPosition="above">
{{ 'alias.add' | translate }}
</button>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

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

@ -18,7 +18,7 @@
<form [formGroup]="attributeFormGroup" (ngSubmit)="add()" style="min-width: 420px; max-width: 420px;">
<mat-toolbar color="primary">
<h2>{{ (isTelemetry ? 'attribute.add-telemetry' : 'attribute.add') | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -46,7 +46,7 @@
</tb-value-input>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

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

@ -18,7 +18,7 @@
<form [formGroup]="addWidgetFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar color="primary">
<h2 translate>attribute.add-widget-to-dashboard</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -27,11 +27,11 @@
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content fxLayout="column">
<div mat-dialog-content class="flex flex-col">
<fieldset [disabled]="isLoading$ | async">
<mat-radio-group formControlName="addToDashboardType">
<mat-radio-button [value]="0" color="primary">
<section fxLayout="column" style="width: 300px;">
<section class="flex flex-col" style="width: 300px;">
<span translate>dashboard.select-existing</span>
<tb-dashboard-autocomplete formControlName="dashboardId"
[required]="addWidgetFormGroup.get('addToDashboardType').value === 0"
@ -40,7 +40,7 @@
</section>
</mat-radio-button>
<mat-radio-button [value]="1" color="primary">
<section fxLayout="column" style="width: 300px;">
<section class="flex flex-col" style="width: 300px;">
<span translate>dashboard.create-new</span>
<mat-form-field class="mat-block">
<mat-label translate>dashboard.new-dashboard-title</mat-label>
@ -55,12 +55,12 @@
</mat-radio-group>
</fieldset>
</div>
<div mat-dialog-actions fxLayout="row">
<div mat-dialog-actions class="flex flex-row">
<mat-checkbox formControlName="openDashboard"
style="margin-bottom: 0;">
{{ 'dashboard.open-dashboard' | translate }}
</mat-checkbox>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

68
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html

@ -16,10 +16,10 @@
-->
<div class="mat-padding tb-entity-table tb-absolute-fill">
<div fxFlex fxLayout="column" class="tb-entity-table-content tb-outlined-border">
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="mode === 'default' && !textSearchMode && dataSource.selection.isEmpty()">
<div class="tb-entity-table-content tb-outlined-border flex flex-1 flex-col">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="mode !== 'default' || textSearchMode || !dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<div class="title-container flex flex-row items-center justify-start xs:flex-col xs:items-start xs:justify-center">
<span class="tb-entity-table-title">{{telemetryTypeTranslationsMap.get(attributeScope) | translate}}</span>
<mat-form-field class="mat-block tb-attribute-scope" *ngIf="!disableAttributeScopeSelection && !attributeScopeSelectionReadonly">
<mat-label translate>attribute.attributes-scope</mat-label>
@ -32,7 +32,7 @@
</mat-select>
</mat-form-field>
</div>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
*ngIf="attributeScope !== attributeScopeTypes.CLIENT_SCOPE"
[disabled]="isLoading$ | async"
@ -58,14 +58,14 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="mode === 'default' && textSearchMode && dataSource.selection.isEmpty()">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="mode !== 'default' || !textSearchMode || !dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<button mat-icon-button
matTooltip="{{ 'action.search' | translate }}"
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<mat-form-field fxFlex>
<mat-form-field class="flex-1">
<mat-label>&nbsp;</mat-label>
<input #searchInput matInput
[formControl]="textSearch"
@ -78,14 +78,14 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar class="mat-mdc-table-toolbar" color="primary" [fxShow]="mode === 'default' && !dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools" fxLayout.xs="row wrap">
<mat-toolbar class="mat-mdc-table-toolbar" color="primary" [class.!hidden]="mode !== 'default' || dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools xs:flex xs:flex-row xs:flex-wrap">
<span class="tb-entity-table-info">
{{ (attributeScope === latestTelemetryTypes.LATEST_TELEMETRY ?
'attribute.selected-telemetry' : 'attribute.selected-attributes') | translate:{count: dataSource.selection.selected.length} }}
</span>
<span fxFlex></span>
<button [fxShow]="attributeScope !== attributeScopeTypes.CLIENT_SCOPE"
<span class="flex-1"></span>
<button [class.!hidden]="attributeScope === attributeScopeTypes.CLIENT_SCOPE"
class="button-widget-action"
mat-icon-button [disabled]="isLoading$ | async"
matTooltip="{{ 'action.delete' | translate }}"
@ -104,12 +104,12 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar class="mat-mdc-table-toolbar" color="primary" [fxShow]="mode === 'widget'">
<div class="mat-toolbar-tools" fxLayoutGap="8px" fxLayout.xs="row wrap">
<div fxFlex fxLayout="row" fxLayoutAlign="start">
<mat-toolbar class="mat-mdc-table-toolbar" color="primary" [class.!hidden]="mode !== 'widget'">
<div class="mat-toolbar-tools gap-2 xs:flex xs:flex-row xs:flex-wrap">
<div class="flex flex-1 flex-row items-stretch justify-start">
<span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span>
<tb-widgets-bundle-select fxFlexOffset="5"
fxFlex
<tb-widgets-bundle-select class="flex-1"
style="margin-left: 5%"
required
[selectFirstBundle]="false"
[selectBundleAlias]="selectedWidgetsBundleAlias"
@ -117,7 +117,7 @@
(ngModelChange)="onWidgetsBundleChanged($event)">
</tb-widgets-bundle-select>
</div>
<button mat-raised-button [fxShow]="widgetsList.length > 0"
<button mat-raised-button [class.!hidden]="!widgetsList.length"
color="accent"
class="button-widget-action"
[disabled]="isLoading$ | async"
@ -136,7 +136,7 @@
</button>
</div>
</mat-toolbar>
<div fxFlex class="table-container" [fxShow]="mode !== 'widget'">
<div class="table-container flex-1" [class.!hidden]="mode === 'widget'">
<table mat-table [dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="select" sticky>
@ -162,7 +162,7 @@
<ng-container matColumnDef="key">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 40%"> {{ 'attribute.key' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let attribute" class="attribute">
<div fxLayout="row" style="align-items: center">
<div class="flex flex-row" style="align-items: center">
<span class="ellipsis">{{ attribute.key }}</span>
<tb-copy-button
class="attribute-copy"
@ -182,8 +182,8 @@
<mat-cell *matCellDef="let attribute"
class="tb-value-cell attribute"
(click)="editAttribute($event, attribute)">
<div fxFlex fxLayout="row" style="align-items: center">
<div fxFlex fxLayout="row" style="overflow: hidden; align-items: center;">
<div class="flex flex-1 flex-row" style="align-items: center">
<div class="flex flex-1 flex-row" style="overflow: hidden; align-items: center;">
<span class="ellipsis">{{attribute.value | tbJson}}</span>
<tb-copy-button
*ngIf="attribute.value !== ''"
@ -197,7 +197,7 @@
>
</tb-copy-button>
</div>
<span [fxShow]="!isClientSideTelemetryTypeMap.get(attributeScope)">
<span [class.!hidden]="isClientSideTelemetryTypeMap.get(attributeScope)">
<mat-icon>edit</mat-icon>
</span>
<button mat-icon-button *ngIf="attributeScope === latestTelemetryTypes.LATEST_TELEMETRY"
@ -207,34 +207,32 @@
</div>
</mat-cell>
</ng-container>
<mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'mat-row-select': true,
'mat-selected': dataSource.selection.isSelected(attribute)}"
<mat-header-row class="mat-row-select" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row class="mat-row-select" [class.mat-selected]="dataSource.selection.isSelected(attribute)"
*matRowDef="let attribute; columns: displayedColumns;" (click)="dataSource.selection.toggle(attribute)"></mat-row>
</table>
<span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center"
class="no-data-found" translate>{{
<span [class.!hidden]="(dataSource.isEmpty() | async) === false"
class="no-data-found flex items-center justify-center" translate>{{
attributeScope === latestTelemetryTypes.LATEST_TELEMETRY
? 'attribute.no-telemetry-text'
: 'attribute.no-attributes-text'
}}</span>
</div>
<mat-divider [fxShow]="mode !== 'widget'"></mat-divider>
<mat-paginator [fxShow]="mode !== 'widget'"
<mat-divider [class.!hidden]="mode === 'widget'"></mat-divider>
<mat-paginator [class.!hidden]="mode === 'widget'"
[length]="dataSource.total() | async"
[pageIndex]="pageLink.page"
[pageSize]="pageLink.pageSize"
[pageSizeOptions]="[10, 20, 30]"
[hidePageSize]="hidePageSize"
showFirstLastButtons></mat-paginator>
<ngx-hm-carousel fxFlex *ngIf="mode === 'widget' && widgetsList.length > 0"
<ngx-hm-carousel *ngIf="mode === 'widget' && widgetsList.length > 0"
#carousel
[(ngModel)]="widgetsCarouselIndex"
(ngModelChange)="onWidgetsCarouselIndexChanged()"
[data]="widgetsList"
[infinite]="false"
class="carousel c-accent">
class="carousel c-accent flex-1">
<section ngx-hm-carousel-container class="content">
<article class="item cursor-pointer"
ngx-hm-carousel-item
@ -277,12 +275,8 @@
<span translate *ngIf="mode === 'widget' && widgetsLoaded &&
widgetsList.length === 0 &&
widgetBundleSet"
fxFlex fxLayoutAlign="center center"
style="display: flex;"
class="mat-headline-5">widgets-bundle.empty</span>
class="mat-headline-5 flex flex-1 items-center justify-center">widgets-bundle.empty</span>
<span translate *ngIf="mode === 'widget' && !widgetBundleSet"
fxFlex fxLayoutAlign="center center"
style="display: flex;"
class="mat-headline-5">widget.select-widgets-bundle</span>
class="mat-headline-5 flex flex-1 items-center justify-center">widget.select-widgets-bundle</span>
</div>
</div>

6
ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.html

@ -17,7 +17,7 @@
-->
<mat-toolbar>
<h2>{{ "attribute.delete-timeseries.delete-strategy" | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
type="button"
(click)="cancel()">
@ -51,8 +51,8 @@
{{ "attribute.delete-timeseries.rewrite-latest-value" | translate }}
</mat-slide-toggle>
</form>
<div fxLayout="row" class="tb-panel-actions">
<span fxFlex></span>
<div class="tb-panel-actions flex flex-row">
<span class="flex-1"></span>
<button mat-button color="primary"
type="button"
(click)="cancel()" cdkFocusInitial>

4
ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html

@ -23,8 +23,8 @@
requiredText="attribute.value-required">
</tb-value-input>
</fieldset>
<div fxLayout="row" class="tb-panel-actions">
<span fxFlex></span>
<div class="tb-panel-actions flex flex-row">
<span class="flex-1"></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

10
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html

@ -17,23 +17,23 @@
-->
<mat-toolbar color="primary">
<h2 translate>audit-log.audit-log-details</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
[mat-dialog-close]="false"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content fxLayout="column">
<div mat-dialog-content class="flex flex-col">
<label translate class="tb-title no-padding">audit-log.action-data</label>
<div #actionDataEditor class="tb-audit-log-action-data">
</div>
<span style="min-height: 30px;"></span>
<label [fxShow]="displayFailureDetails" translate class="tb-title no-padding">audit-log.failure-details</label>
<div #failureDetailsEditor [fxShow]="displayFailureDetails" class="tb-audit-log-failure-details">
<label [class.!hidden]="!displayFailureDetails" translate class="tb-title no-padding">audit-log.failure-details</label>
<div #failureDetailsEditor [class.!hidden]="!displayFailureDetails" class="tb-audit-log-failure-details">
</div>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

2
ui-ngx/src/app/modules/home/components/audit-log/audit-log-table.component.html

@ -15,4 +15,4 @@
limitations under the License.
-->
<tb-entities-table [entitiesTableConfig]="auditLogTableConfig" [ngClass]="{'tb-details-mode': detailsMode}"></tb-entities-table>
<tb-entities-table [entitiesTableConfig]="auditLogTableConfig" [class.tb-details-mode]="detailsMode"></tb-entities-table>

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

@ -19,8 +19,8 @@
<mat-toolbar color="primary">
<div class="tb-add-widget-toolbar">
<h2>{{'widget.add' | translate}}:</h2>
<span fxFlex>{{data.widgetInfo.widgetName}}</span>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap.gt-xs="16px" style="min-width: fit-content;">
<span class="flex-1">{{data.widgetInfo.widgetName}}</span>
<div class="flex flex-row items-center justify-start gt-xs:gap-4" style="min-width: fit-content;">
<tb-toggle-select *ngIf="widgetConfig.hasBasicMode" appearance="fill-invert" selectMediaBreakpoint="xs"
[(ngModel)]="widgetConfigMode" [ngModelOptions]="{standalone: true}">
<tb-toggle-option value="basic">{{ 'widget.basic-mode' | translate }}</tb-toggle-option>
@ -66,14 +66,14 @@
</div>
</tb-widget-preview>
</div>
<div mat-dialog-actions fxLayoutAlign="space-between center">
<div mat-dialog-actions class="flex items-center justify-between">
<button mat-button color="primary"
type="button"
(click)="cancel()"
cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<div fxLayout="row" fxLayoutGap="8px">
<div class="flex flex-row gap-2">
<button *ngIf="!previewMode"
mat-button color="primary"
type="button"

15
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.html

@ -18,7 +18,7 @@
<form (ngSubmit)="save()">
<mat-toolbar color="primary">
<h2 translate>dashboard.update-image</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -28,13 +28,13 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content style="position: relative;">
<fieldset [disabled]="(isLoading$ | async) || (takingScreenshot$ | async)" fxLayout="column" fxLayoutAlign="center stretch" fxLayoutGap="8px">
<div [formGroup]="dashboardRectFormGroup" fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center center">
<fieldset [disabled]="(isLoading$ | async) || (takingScreenshot$ | async)" class="flex flex-col items-stretch justify-center gap-2">
<div [formGroup]="dashboardRectFormGroup" class="flex flex-col items-center justify-center gap-2">
<mat-form-field class="rect-field">
<mat-label>Top %</mat-label>
<input matInput formControlName="top" type="number" step="1" min="0" max="100">
</mat-form-field>
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="center center">
<div class="flex flex-row items-center justify-center gap-2">
<mat-form-field class="rect-field">
<mat-label>Left %</mat-label>
<input matInput formControlName="left" type="number" step="1" min="0" max="100">
@ -53,7 +53,7 @@
<input matInput formControlName="bottom" type="number" step="1" min="0" max="100">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutAlign="center center">
<div class="flex flex-row items-center justify-center">
<button mat-raised-button color="accent"
type="button"
[disabled]="(isLoading$ | async) || (takingScreenshot$ | async)"
@ -67,12 +67,11 @@
</tb-gallery-image-input>
</div>
</fieldset>
<div *ngIf="takingScreenshot$ | async" class="taking-screenshot-progress tb-absolute-fill" fxLayout="column"
fxLayoutAlign="center center">
<div *ngIf="takingScreenshot$ | async" class="taking-screenshot-progress tb-absolute-fill flex flex-col items-center justify-center">
<mat-progress-spinner color="accent" mode="indeterminate"></mat-progress-spinner>
</div>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async) || (takingScreenshot$ | async)"

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

@ -15,32 +15,29 @@
limitations under the License.
-->
<div class="tb-dashboard-page mat-content" [ngClass]="{'mobile-app': isMobileApp && !isEdit}" tb-toast
fxFlex tb-fullscreen [fullscreenElement]="elRef.nativeElement" [fullscreen]="widgetEditMode || iframeMode || forceFullscreen || isFullscreen">
<div class="tb-dashboard-page mat-content flex-1" [class.mobile-app]="isMobileApp && !isEdit" tb-toast
tb-fullscreen [fullscreenElement]="elRef.nativeElement" [fullscreen]="widgetEditMode || iframeMode || forceFullscreen || isFullscreen">
<tb-hotkeys-cheatsheet #cheatSheetComponent></tb-hotkeys-cheatsheet>
<section class="tb-dashboard-toolbar"
[ngClass]="{ 'tb-dashboard-toolbar-opened': toolbarOpened,
'tb-dashboard-toolbar-closed': !toolbarOpened }">
<tb-dashboard-toolbar [fxShow]="!widgetEditMode && !hideToolbar" [forceFullscreen]="forceFullscreen"
[class.tb-dashboard-toolbar-opened]="toolbarOpened"
[class.tb-dashboard-toolbar-closed]="!toolbarOpened">
<tb-dashboard-toolbar [class.!hidden]="widgetEditMode || hideToolbar" [forceFullscreen]="forceFullscreen"
[toolbarOpened]="toolbarOpened" (triggerClick)="openToolbar()">
<div class="tb-dashboard-action-panels" fxLayout="column" fxLayout.gt-sm="row"
fxLayoutAlign="center stretch" fxLayoutAlign.gt-sm="space-between center">
<div class="tb-dashboard-action-panel tb-left-panel" [fxHide]="isMobileApp && !isEdit">
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.gt-sm displayUserInfo="true">
<div class="tb-dashboard-action-panels flex flex-col items-stretch justify-center gt-sm:flex-row gt-sm:items-center gt-sm:justify-between">
<div class="tb-dashboard-action-panel tb-left-panel" [class.!hidden]="isMobileApp && !isEdit">
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" class="gt-sm:!hidden" displayUserInfo="true">
</tb-user-menu>
<ng-container *ngIf="isEdit">
<div style="flex: 1 1 100%" fxHide.gt-sm></div>
<div fxFlex.lt-md fxLayout="row"
fxLayoutAlign.gt-sm="start center"
fxLayoutAlign="end center" fxLayoutGap.lt-lg="3px" fxLayoutGap.lg="6px" fxLayoutGap.gt-lg="12px">
<div style="flex: 1 1 100%" class="gt-sm:!hidden"></div>
<div class="flex flex-row items-center justify-end lg:gap-1.5 lt-md:flex-1 lt-lg:gap-0.75 gt-sm:items-center gt-sm:justify-start gt-lg:gap-3">
<mat-divider class="tb-toolbar-divider" vertical></mat-divider>
<button fxHide.gt-md mat-icon-button
<button class="gt-md:!hidden" mat-icon-button
matTooltip="{{'layout.manage' | translate}}"
matTooltipPosition="below"
(click)="manageDashboardLayouts($event)">
<mat-icon>view_compact</mat-icon>
</button>
<button fxHide.lt-lg mat-button
<button class="lt-lg:!hidden" mat-button
matTooltip="{{'layout.manage' | translate}}"
matTooltipPosition="below"
(click)="manageDashboardLayouts($event)">
@ -52,7 +49,7 @@
</tb-select-dashboard-breakpoint>
</div>
</ng-container>
<tb-states-component fxFlex.lt-md
<tb-states-component class="lt-md:flex-1"
[statesControllerId]="isEdit ? 'default' : dashboardConfiguration.settings.stateControllerId"
[dashboardCtrl]="this"
[dashboardId]="setStateDashboardId ? dashboard.id.id : ''"
@ -63,13 +60,13 @@
[states]="dashboardConfiguration.states">
</tb-states-component>
<ng-container *ngIf="isEdit">
<button fxHide.gt-md mat-icon-button
<button class="gt-md:!hidden" mat-icon-button
matTooltip="{{'dashboard.manage-states' | translate}}"
matTooltipPosition="below"
(click)="manageDashboardStates($event)">
<mat-icon>layers</mat-icon>
</button>
<button fxHide.lt-lg mat-button
<button class="lt-lg:!hidden" mat-button
matTooltip="{{'dashboard.manage-states' | translate}}"
matTooltipPosition="below"
(click)="manageDashboardStates($event)">
@ -81,65 +78,65 @@
aria-label="dashboard_logo" class="dashboard_logo"/>
</div>
<div class="tb-dashboard-action-panel">
<button [fxShow]="showCloseToolbar()" mat-icon-button class="close-action"
<button [class.!hidden]="!showCloseToolbar()" mat-icon-button class="close-action"
matTooltip="{{ 'dashboard.close-toolbar' | translate }}"
matTooltipPosition="below"
(click)="closeToolbar()">
<mat-icon>arrow_forward</mat-icon>
</button>
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" fxHide.lt-md displayUserInfo="true">
<tb-user-menu *ngIf="!isPublicUser() && forceFullscreen" class="lt-md:!hidden" displayUserInfo="true">
</tb-user-menu>
<button [fxShow]="showRightLayoutSwitch()" mat-icon-button
<button [class.!hidden]="!showRightLayoutSwitch()" mat-icon-button
matTooltip="{{ (isRightLayoutOpened ? 'dashboard.hide-details' : 'dashboard.show-details') | translate }}"
matTooltipPosition="below"
(click)="toggleLayouts()">
<mat-icon>{{isRightLayoutOpened ? 'arrow_back' : 'menu'}}</mat-icon>
</button>
<button [fxShow]="!hideFullscreenButton()" mat-icon-button fxHide.xs
<button [class.!hidden]="hideFullscreenButton()" class="xs:!hidden" mat-icon-button
matTooltip="{{(isFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
matTooltipPosition="below"
(click)="isFullscreen = !isFullscreen">
<mat-icon>{{ isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
</button>
<button [fxShow]="currentDashboardId && !isMobileApp && displayExport() && !isEdit" mat-icon-button
<button [class.!hidden]="!currentDashboardId || isMobileApp || !displayExport() || isEdit" mat-icon-button
matTooltip="{{'dashboard.export' | translate}}"
matTooltipPosition="below"
(click)="exportDashboard($event)">
<mat-icon>file_download</mat-icon>
</button>
<ng-container *ngIf="!readonly && !isEdit">
<button fxHide.gt-md mat-icon-button (click)="toggleDashboardEditMode()"
<button class="gt-md:!hidden" mat-icon-button (click)="toggleDashboardEditMode()"
matTooltip="{{'dashboard.edit-mode' | translate}}"
matTooltipPosition="below">
<mat-icon>edit</mat-icon></button>
<button fxHide.lt-lg mat-stroked-button (click)="toggleDashboardEditMode()">
<button class="lt-lg:!hidden" mat-stroked-button (click)="toggleDashboardEditMode()">
<mat-icon>edit</mat-icon>{{'dashboard.edit-mode' | translate}}</button>
</ng-container>
<ng-container *ngIf="isEdit">
<button fxHide.gt-md mat-icon-button (click)="saveDashboard()" [disabled]="isLoading$ | async"
<button class="gt-md:!hidden" mat-icon-button (click)="saveDashboard()" [disabled]="isLoading$ | async"
matTooltip="{{'action.save' | translate}}"
matTooltipPosition="below">
<mat-icon>done</mat-icon></button>
<button fxHide.lt-lg mat-stroked-button [disabled]="isLoading$ | async" (click)="saveDashboard()">
<button class="lt-lg:!hidden" mat-stroked-button [disabled]="isLoading$ | async" (click)="saveDashboard()">
<mat-icon>done</mat-icon>{{ 'action.save' | translate }}</button>
<button fxHide.gt-md mat-icon-button (click)="toggleDashboardEditMode()"
<button class="gt-md:!hidden" mat-icon-button (click)="toggleDashboardEditMode()"
matTooltip="{{'action.cancel' | translate}}"
[disabled]="isLoading$ | async"
matTooltipPosition="below">
<mat-icon>close</mat-icon></button>
<button fxHide.lt-lg mat-button (click)="toggleDashboardEditMode()" [disabled]="isLoading$ | async">
<button class="lt-lg:!hidden" mat-button (click)="toggleDashboardEditMode()" [disabled]="isLoading$ | async">
<mat-icon>close</mat-icon>{{ 'action.cancel' | translate }}</button>
<mat-divider class="tb-toolbar-divider" vertical></mat-divider>
</ng-container>
<ng-container *ngIf="currentDashboardId && isEdit && isTenantAdmin()">
<button fxHide.lt-lg fxHide.gt-lg mat-icon-button
<button class="lt-lg:!hidden gt-lg:!hidden" mat-icon-button
#versionControlIconButton
matTooltip="{{'version-control.version-control' | translate}}"
matTooltipPosition="below"
(click)="toggleVersionControl($event, versionControlIconButton)">
<mat-icon>history</mat-icon>
</button>
<button fxHide.lt-xl mat-button
<button class="lt-xl:!hidden" mat-button
#versionControlButton
matTooltip="{{'version-control.version-control' | translate}}"
matTooltipPosition="below"
@ -149,13 +146,13 @@
</button>
</ng-container>
<ng-container *ngIf="isEdit">
<button fxHide.gt-lg mat-icon-button
<button class="gt-lg:!hidden" mat-icon-button
matTooltip="{{ 'filter.filters' | translate }}"
matTooltipPosition="below"
(click)="openFilters($event)">
<mat-icon>filter_list</mat-icon>
</button>
<button fxHide.lt-xl mat-button
<button class="lt-xl:!hidden" mat-button
matTooltip="{{ 'filter.filters' | translate }}"
matTooltipPosition="below"
(click)="openFilters($event)">
@ -164,13 +161,13 @@
</button>
</ng-container>
<ng-container *ngIf="isEdit">
<button fxHide.gt-lg mat-icon-button
<button class="gt-lg:!hidden" mat-icon-button
matTooltip="{{ 'entity.aliases' | translate }}"
matTooltipPosition="below"
(click)="openEntityAliases($event)">
<mat-icon>devices_other</mat-icon>
</button>
<button fxHide.lt-xl mat-button
<button class="lt-xl:!hidden" mat-button
matTooltip="{{ 'entity.aliases' | translate }}"
matTooltipPosition="below"
(click)="openEntityAliases($event)">
@ -179,13 +176,13 @@
</button>
</ng-container>
<ng-container *ngIf="isEdit">
<button fxHide.gt-lg mat-icon-button
<button class="gt-lg:!hidden" mat-icon-button
matTooltip="{{ 'dashboard.settings' | translate }}"
matTooltipPosition="below"
(click)="openDashboardSettings($event)">
<mat-icon>settings</mat-icon>
</button>
<button fxHide.lt-xl mat-button
<button class="lt-xl:!hidden" mat-button
matTooltip="{{ 'dashboard.settings' | translate }}"
matTooltipPosition="below"
(click)="openDashboardSettings($event)">
@ -193,14 +190,14 @@
{{ 'dashboard.settings' | translate }}
</button>
</ng-container>
<button [fxShow]="currentDashboardId && !isEdit && !isMobileApp && isTenantAdmin() && displayUpdateDashboardImage()" mat-icon-button
<button [class.!hidden]="!currentDashboardId || isEdit || isMobileApp || !isTenantAdmin() || !displayUpdateDashboardImage()" mat-icon-button
matTooltip="{{'dashboard.update-image' | translate}}"
matTooltipPosition="below"
(click)="updateDashboardImage($event)">
<mat-icon>wallpaper</mat-icon>
</button>
<ng-container *ngIf="isEdit; else timewindowTemplate">
<tb-timewindow fxHide.lt-xl asButton
<tb-timewindow class="lt-xl:!hidden" asButton
flatButton
[displayTimewindowValue]="false"
[isEdit]="true"
@ -209,7 +206,7 @@
timezone="true"
[(ngModel)]="dashboardCtx.dashboardTimewindow">
</tb-timewindow>
<tb-timewindow fxHide.gt-lg
<tb-timewindow class="gt-lg:!hidden"
hideLabel
[isEdit]="true"
tooltipPosition="below"
@ -220,7 +217,7 @@
</ng-container>
<ng-template #timewindowTemplate>
<ng-container *ngIf="displayDashboardTimewindow()">
<tb-timewindow fxHide.gt-md
<tb-timewindow class="gt-md:!hidden"
[isEdit]="false"
hideLabel
tooltipPosition="below"
@ -228,7 +225,7 @@
timezone="true"
[(ngModel)]="dashboardCtx.dashboardTimewindow">
</tb-timewindow>
<tb-timewindow fxHide.lt-lg
<tb-timewindow class="lt-lg:!hidden"
[isEdit]="false"
tooltipPosition="below"
aggregation="true"
@ -238,13 +235,13 @@
</ng-container>
</ng-template>
<ng-container *ngIf="isEdit">
<button fxHide.gt-md mat-icon-button
<button class="gt-md:!hidden" mat-icon-button
matTooltip="{{ 'dashboard.add-widget-button-text' | translate }}"
matTooltipPosition="below"
(click)="addWidget($event)">
<mat-icon>add</mat-icon>
</button>
<button fxHide.lt-lg mat-stroked-button
<button class="lt-lg:!hidden" mat-stroked-button
matTooltip="{{ 'dashboard.add-widget-button-text' | translate }}"
matTooltipPosition="below"
(click)="addWidget($event)">
@ -252,11 +249,11 @@
{{ 'dashboard.add-widget-button-text' | translate }}
</button>
</ng-container>
<tb-filters-edit [fxShow]="!isEdit && displayFilters()"
<tb-filters-edit [class.!hidden]="isEdit || !displayFilters()"
tooltipPosition="below"
[aliasController]="dashboardCtx.aliasController">
</tb-filters-edit>
<tb-aliases-entity-select [fxShow]="!isEdit && displayEntitiesSelect()"
<tb-aliases-entity-select [class.!hidden]="isEdit || !displayEntitiesSelect()"
tooltipPosition="below"
[aliasController]="dashboardCtx.aliasController">
</tb-aliases-entity-select>
@ -273,30 +270,30 @@
<section class="tb-dashboard-container tb-absolute-fill"
tb-toast toastTarget="dashboardRoot"
#dashboardContainer
[ngClass]="{ 'is-fullscreen': forceFullscreen,
'tb-dashboard-toolbar-opened': toolbarOpened,
'tb-dashboard-toolbar-animated': isToolbarOpenedAnimate,
'tb-dashboard-toolbar-closed': !toolbarOpened }">
[class.is-fullscreen]="forceFullscreen"
[class.tb-dashboard-toolbar-opened]="toolbarOpened"
[class.tb-dashboard-toolbar-animated]="isToolbarOpenedAnimate"
[class.tb-dashboard-toolbar-closed]="!toolbarOpened">
<mat-drawer-container hasBackdrop="false" class="tb-absolute-fill tb-dashboard-drawer-container">
<mat-drawer-content #dashboardContent fxLayout="column" fxLayoutAlign="center start">
<mat-drawer-content #dashboardContent class="flex flex-col items-start justify-center">
<section *ngIf="!widgetEditMode" class="tb-dashboard-title"
[ngStyle]="{'color': dashboard.configuration.settings.titleColor}">
<h3 [fxShow]="!isEdit && displayTitle()">{{ translatedDashboardTitle }}</h3>
<mat-form-field [fxShow]="isEdit" class="mat-block tb-appearance-transparent" subscriptSizing="dynamic">
<mat-label translate [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">dashboard.title</mat-label>
[style.color]="dashboard.configuration.settings.titleColor">
<h3 [class.!hidden]="isEdit || !displayTitle()">{{ translatedDashboardTitle }}</h3>
<mat-form-field [class.!hidden]="!isEdit" class="mat-block tb-appearance-transparent" subscriptSizing="dynamic">
<mat-label translate [style.color]="dashboard.configuration.settings.titleColor">dashboard.title</mat-label>
<input matInput class="tb-dashboard-title"
[ngStyle]="{'color': dashboard.configuration.settings.titleColor}"
[style.color]="dashboard.configuration.settings.titleColor"
required name="title" [(ngModel)]="dashboard.title">
</mat-form-field>
</section>
<mat-drawer-container class="tb-dashboard-layouts" fxFlex
[class]="{'tb-shrinked' : isEditingWidget}">
<mat-drawer-container class="tb-dashboard-layouts flex-1"
[class.tb-shrinked]="isEditingWidget">
<mat-drawer *ngIf="layouts.right.show"
id="tb-right-layout"
[ngStyle]="{minWidth: rightLayoutSize.width,
maxWidth: rightLayoutSize.width,
height: rightLayoutSize.height,
borderLeft: 'none'}"
style="border-left: none"
[style.min-width]="rightLayoutSize.width"
[style.max-width]="rightLayoutSize.width"
[style.height]="rightLayoutSize.height"
disableClose="true"
[@.disabled]="!isMobile"
position="end"
@ -314,10 +311,10 @@
[popoverComponent]="popoverComponent">
</tb-dashboard-layout>
</mat-drawer>
<mat-drawer-content [fxShow]="layouts.main.show"
<mat-drawer-content [class.!hidden]="!layouts.main.show"
id="tb-main-layout"
[style]="{width: mainLayoutSize.width,
height: mainLayoutSize.height}">
[style.width]="mainLayoutSize.width"
[style.height]="mainLayoutSize.height">
<tb-dashboard-layout
[style.max-width]="mainLayoutSize.maxWidth"
[style.min-width]="mainLayoutSize.minWidth"
@ -333,13 +330,13 @@
</tb-dashboard-layout>
<div class="layout-filler"
*ngIf="isEdit"
[fxShow]="showMainLayoutFiller">
[class.!hidden]="!showMainLayoutFiller">
{{ 'dashboard.breakpoint-value' | translate : {value: currentBreakpointValue } }}
</div>
</mat-drawer-content>
</mat-drawer-container>
<section data-html2canvas-ignore fxLayout="row" class="layout-wrap tb-header-buttons tb-enter-edit-mode" fxLayoutAlign="start end" *ngIf="!readonly && (hideToolbar || widgetEditMode)">
<button [fxShow]="!isEdit"
<section data-html2canvas-ignore class="tb-header-buttons tb-enter-edit-mode flex flex-row flex-wrap items-end justify-start" *ngIf="!readonly && (hideToolbar || widgetEditMode)">
<button [class.!hidden]="isEdit"
color="primary"
mat-mini-fab
class="tb-btn-header tb-btn-edit"
@ -348,7 +345,7 @@
matTooltipPosition="above">
<mat-icon>edit</mat-icon>
</button>
<button [fxShow]="isEdit"
<button [class.!hidden]="!isEdit"
color="primary"
mat-mini-fab
class="tb-btn-header tb-btn-edit"
@ -357,7 +354,7 @@
matTooltip="{{'action.save' | translate}}"
matTooltipPosition="below">
<mat-icon>done</mat-icon></button>
<button [fxShow]="isEdit"
<button [class.!hidden]="!isEdit"
color="primary"
mat-mini-fab
class="tb-btn-header tb-btn-edit"
@ -367,7 +364,7 @@
matTooltipPosition="below">
<mat-icon>close</mat-icon></button>
</section>
<section *ngIf="!embedded" data-html2canvas-ignore class="tb-powered-by-footer" [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">
<section *ngIf="!embedded" data-html2canvas-ignore class="tb-powered-by-footer" [style.color]="dashboard.configuration.settings.titleColor">
<span>Powered by <a href="https://thingsboard.io" target="_blank">Thingsboard v.{{ thingsboardVersion }}</a></span>
</section>
</mat-drawer-content>
@ -378,12 +375,12 @@
disableClose
mode="over"
position="end">
<tb-details-panel *ngIf="!isEditingWidgetClosed" fxFlex
<tb-details-panel *ngIf="!isEditingWidgetClosed" class="flex-1"
headerTitle="{{editingWidget?.config.title}}"
headerSubtitle="{{ editingWidgetSubtitle }}"
[isReadOnly]="true"
(closeDetails)="onEditWidgetClosed()">
<div class="details-buttons" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div class="details-buttons flex flex-row items-center justify-start gap-4">
<tb-toggle-select *ngIf="tbEditWidget.widgetConfig?.hasBasicMode" appearance="fill-invert" selectMediaBreakpoint="xs" [(ngModel)]="tbEditWidget.widgetConfigMode">
<tb-toggle-option value="basic">{{ 'widget.basic-mode' | translate }}</tb-toggle-option>
<tb-toggle-option value="advanced">{{ 'widget.advanced-mode' | translate }}</tb-toggle-option>
@ -403,7 +400,7 @@
(applyWidgetConfig)="saveWidget()">
</tb-edit-widget>
</tb-details-panel>
<tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" fxFlex
<tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" class="flex-1"
headerTitle="{{ isAddingWidget ?
((dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets' ? ('dashboard.select-widget-value' | translate: { title: ('widget.all-widgets' | translate) }) :
(!dashboardWidgetSelectComponent?.widgetsBundle ?
@ -416,19 +413,19 @@
backgroundColor="#cfd8dc"
(closeDetails)="onAddWidgetClosed()"
(closeSearch)="dashboardWidgetSelectComponent.search = ''">
<div class="prefix-title-buttons" [fxShow]="!!(isAddingWidget && (dashboardWidgetSelectComponent?.widgetsBundle || dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets'))" style="height: 28px; margin-right: 12px">
<div class="prefix-title-buttons" [class.!hidden]="!(isAddingWidget && (dashboardWidgetSelectComponent?.widgetsBundle || dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets'))" style="height: 28px; margin-right: 12px">
<button class="tb-mat-28" mat-icon-button type="button" (click)="clearSelectedWidgetBundle()">
<mat-icon>arrow_back</mat-icon>
</button>
</div>
<div class="search-pane" *ngIf="isAddingWidget && dashboardWidgetSelectComponent" fxLayout="row">
<tb-widgets-bundle-search fxFlex
<div class="search-pane flex flex-row" *ngIf="isAddingWidget && dashboardWidgetSelectComponent">
<tb-widgets-bundle-search class="flex-1"
[(ngModel)]="dashboardWidgetSelectComponent.search"
placeholder="{{ ((!dashboardWidgetSelectComponent.widgetsBundle && dashboardWidgetSelectComponent.selectWidgetMode === 'bundles')
? 'widgets-bundle.search' : 'widget.search') | translate }}">
</tb-widgets-bundle-search>
</div>
<div class="details-buttons" *ngIf="isAddingWidget" fxLayout="row" fxLayoutAlign="start center">
<div class="details-buttons flex flex-row items-center justify-start" *ngIf="isAddingWidget">
<button mat-button type="button" (click)="importWidget($event)"
*ngIf="dashboardWidgetSelectComponent?.selectWidgetMode === 'bundles' && !dashboardWidgetSelectComponent?.widgetsBundle">
<mat-icon>file_upload</mat-icon>{{ 'dashboard.import-widget' | translate }}</button>

10
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html

@ -18,7 +18,7 @@
<form (ngSubmit)="save()" style="width: 750px;">
<mat-toolbar color="primary">
<h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -58,7 +58,7 @@
{{ 'dashboard.display-dashboard-logo' | translate }}
</mat-slide-toggle>
</div>
<tb-gallery-image-input fxFlex
<tb-gallery-image-input class="flex-1"
label="{{'dashboard.dashboard-logo-image' | translate}}"
formControlName="dashboardLogoUrl">
</tb-gallery-image-input>
@ -92,7 +92,7 @@
</div>
<mat-expansion-panel class="tb-settings">
<mat-expansion-panel-header>
<mat-panel-description fxLayoutAlign="end" translate>
<mat-panel-description class="flex items-stretch justify-end" translate>
dashboard.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
@ -197,7 +197,7 @@
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-gallery-image-input fxFlex
<tb-gallery-image-input class="flex-1"
label="{{'dashboard.background-image' | translate}}"
formControlName="backgroundImageUrl">
</tb-gallery-image-input>
@ -244,7 +244,7 @@
</div>
</div>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

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

@ -16,7 +16,8 @@
-->
<mat-fab-toolbar [isOpen]="toolbarOpened" color="primary" direction="left"
[ngClass]="{'is-fullscreen': forceFullscreen, 'mat-elevation-z1': forceFullscreen && toolbarOpened }">
[class.is-fullscreen]="forceFullscreen"
[class.mat-elevation-z1]="forceFullscreen && toolbarOpened">
<mat-fab-trigger>
<button mat-fab color="primary"
[matTooltip]="!toolbarOpened ? ('dashboard.open-toolbar' | translate) : ''"

20
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html

@ -36,7 +36,7 @@
[noData]="noWidgets">
</tb-scroll-grid>
<ng-template #widgetCard let-item="item">
<mat-card class="tb-widget-preview-card" appearance="raised" fxLayout="column" fxLayoutGap="8px" (click)="onWidgetClicked($event, item)">
<mat-card class="tb-widget-preview-card flex flex-col gap-2" appearance="raised" (click)="onWidgetClicked($event, item)">
<div class="title-container">
<div title="{{item.name}}" class="widget-title">
{{item.name}}
@ -56,8 +56,7 @@
</mat-card>
</ng-template>
<ng-template #loadingWidgets>
<div fxLayout="column"
fxLayoutAlign="center center" class="tb-absolute-fill">
<div class="tb-absolute-fill flex flex-col items-center justify-center">
<span class="mat-headline-5" style="padding-bottom: 20px;">
{{ 'widget.loading-widgets' | translate }}
</span>
@ -65,9 +64,7 @@
</div>
</ng-template>
<ng-template #noWidgets>
<span style="display: flex;"
fxLayoutAlign="center center"
class="mat-headline-5 tb-absolute-fill">{{(selectWidgetMode === 'bundles' ? 'widgets-bundle.empty' : 'widget.no-widgets-text') | translate}}</span>
<span class="mat-headline-5 tb-absolute-fill flex items-center justify-center">{{(selectWidgetMode === 'bundles' ? 'widgets-bundle.empty' : 'widget.no-widgets-text') | translate}}</span>
</ng-template>
</ng-template>
@ -83,7 +80,7 @@
[noData]="noWidgetBundles">
</tb-scroll-grid>
<ng-template #widgetsBundleCard let-item="item">
<mat-card class="tb-widget-preview-card" appearance="raised" fxLayout="column" fxLayoutGap="8px" (click)="selectBundle($event, item)">
<mat-card class="tb-widget-preview-card flex flex-col gap-2" appearance="raised" (click)="selectBundle($event, item)">
<div class="title-container">
<div title="{{item.name}}" class="widget-title">
{{item.title}}
@ -100,8 +97,7 @@
</mat-card>
</ng-template>
<ng-template #loadingWidgetBundles>
<div fxLayout="column"
fxLayoutAlign="center center" class="tb-absolute-fill">
<div class="tb-absolute-fill flex flex-col items-center justify-center">
<span class="mat-headline-5" style="padding-bottom: 20px;">
{{ 'widgets-bundle.loading-widgets-bundles' | translate }}
</span>
@ -110,13 +106,11 @@
</ng-template>
<ng-template #noWidgetBundles>
<span translate
style="display: flex;"
fxLayoutAlign="center center"
class="mat-headline-5 tb-absolute-fill">widgets-bundle.no-widgets-bundles-text</span>
class="mat-headline-5 tb-absolute-fill flex items-center justify-center">widgets-bundle.no-widgets-bundles-text</span>
</ng-template>
</ng-template>
<ng-template #widgetLoadingCard>
<mat-card class="tb-widget-preview-card loading-cell" appearance="raised" fxLayout="column" fxLayoutGap="8px">
<mat-card class="tb-widget-preview-card loading-cell flex flex-col gap-2" appearance="raised">
<div class="title-container">
</div>
<div class="preview-container">

8
ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html

@ -35,7 +35,7 @@
{{ 'action.back' | translate }}
</button>
<button *ngIf="!previewMode"
fxHide.gt-sm
class="gt-sm:!hidden"
[disabled]="!widgetFormGroup.valid"
mat-button color="primary"
(click)="previewMode = true">
@ -43,16 +43,16 @@
{{ 'widget-config.preview' | translate }}
</button>
</div>
<div fxFlex.lt-md class="tb-widget-config-header-suffix" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="12px">
<div class="tb-widget-config-header-suffix flex flex-row items-center justify-end gap-3 lt-md:flex-1">
<button *ngIf="!previewMode"
fxHide.lt-md
class="lt-md:!hidden"
[disabled]="!widgetFormGroup.valid"
mat-button color="primary"
(click)="previewMode = true">
<mat-icon>visibility</mat-icon>
{{ 'widget-config.preview' | translate }}
</button>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="12px">
<div class="flex flex-row items-center justify-start gap-3">
<button [disabled]="!widgetFormGroup.dirty"
mat-stroked-button color="primary"
(click)="onRevertWidgetConfig()">

4
ui-ngx/src/app/modules/home/components/dashboard-page/layout/add-new-breakpoint-dialog.component.html

@ -17,7 +17,7 @@
-->
<mat-toolbar color="primary">
<h2 translate>layout.add-new-breakpoint</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -50,7 +50,7 @@
</mat-form-field>
</form>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button
color="primary"
type="button"

12
ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxLayout="column" class="tb-progress-cover" fxLayoutAlign="center center"
<div class="tb-progress-cover flex flex-col items-center justify-center"
*ngIf="layoutCtx.widgets.isLoading()">
<mat-spinner color="warn" mode="indeterminate" diameter="100">
</mat-spinner>
@ -23,11 +23,11 @@
<div class="mat-content"
style="position: relative; width: 100%; height: 100%;" [tb-hotkeys]="hotKeys"
[cheatSheet]="dashboardCheatSheet"
[style.backgroundImage]="backgroundImage$ | async"
[ngStyle]="dashboardStyle">
<section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center"
style="display: flex; z-index: 1; pointer-events: none;"
class="mat-headline-5 tb-absolute-fill">
[style.background-image]="backgroundImage$ | async"
[style]="dashboardStyle">
<section *ngIf="layoutCtx.widgets.isEmpty()"
style="z-index: 1; pointer-events: none;"
class="mat-headline-5 tb-absolute-fill flex items-center justify-center">
<span *ngIf="!isEdit" style="text-align: center;">
{{'dashboard.no-widgets' | translate}}
</span>

51
ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.html

@ -18,7 +18,7 @@
<form [formGroup]="layoutsFormGroup" (ngSubmit)="save()">
<mat-toolbar color="primary">
<h2 translate>layout.manage</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -80,7 +80,7 @@
</ng-container>
</div>
</div>
<button mat-button fxFlex class="tb-add-breakpoint-button"
<button mat-button class="tb-add-breakpoint-button flex-1"
matTooltip="{{ 'layout.add-new-breakpoint' | translate }}"
matTooltipPosition="above"
color="primary"
@ -92,25 +92,24 @@
</div>
</section>
<ng-template #dividerLayout>
<div fxLayout="column" fxLayoutAlign="start center" fxLayoutGap="20px">
<div class="flex flex-col items-center justify-start gap-5">
<mat-button-toggle-group aria-label="Select width value type"
formControlName="type"
[fxShow]="isDividerLayout">
<mat-button-toggle fxFlex value="percentage">
[class.!hidden]="!isDividerLayout">
<mat-button-toggle class="flex-1" value="percentage">
{{ 'layout.percentage-width' | translate }}
</mat-button-toggle>
<mat-button-toggle fxFlex value="fixed">
<mat-button-toggle class="flex-1" value="fixed">
{{ 'layout.fixed-width' | translate }}
</mat-button-toggle>
</mat-button-toggle-group>
<div fxLayout="row" fxLayoutAlign="center center" class="tb-layout-preview">
<div fxLayout="row"
fxLayoutAlign="start center"
class="tb-layout-preview-container"
<div class="tb-layout-preview flex flex-row items-center justify-center">
<div class="tb-layout-preview-container flex flex-row items-center justify-start"
#tooltip="matTooltip"
matTooltip=""
matTooltipPosition="below">
<div fxLayout="column" fxLayoutAlign="start center" fxFlex="{{ buttonFlexValue() }}" class="tb-layout-preview-container-main">
<div [style.flex-basis]="buttonFlexValue() + '%'" [style.max-width]="buttonFlexValue() + '%'"
class="tb-layout-preview-container-main flex flex-1 flex-col items-center justify-start">
<button mat-icon-button
type="button"
matTooltip="{{ 'layout.settings' | translate }}"
@ -118,25 +117,24 @@
(click)="openLayoutSettings('main')"
class="tb-layout-preview-element"
aria-label="Layout settings"
[ngClass]="layoutButtonClass('main')">
[class]="layoutButtonClass('main')">
<mat-icon>settings</mat-icon>
</button>
<button fxFlex
type="button"
<button type="button"
mat-raised-button
color="primary"
class="tb-layout-button"
class="tb-layout-button flex-1"
(mouseover)="mainLayoutTooltip.show()"
(mouseleave)="mainLayoutTooltip.hide()"
(click)="setFixedLayout('main')"
[ngClass]="layoutButtonClass('main', true)">
[class]="layoutButtonClass('main', true)">
<span [matTooltip]="layoutButtonText('main')"
#mainLayoutTooltip="matTooltip"
matTooltipPosition="above">
{{ (isDividerLayout ? 'layout.left' : 'layout.main') | translate }}
</span>
</button>
<div fxFlex class="tb-layout-preview-element tb-layout-preview-input" *ngIf="showPreviewInputs('main')">
<div class="tb-layout-preview-element tb-layout-preview-input flex-1" *ngIf="showPreviewInputs('main')">
<input *ngIf="layoutsFormGroup.get('type').value !== layoutWidthType.FIXED"
formControlName="leftWidthPercentage"
type="number"
@ -154,7 +152,7 @@
required>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="start center" fxFlex *ngIf="isDividerLayout" matTooltip="">
<div class="flex flex-1 flex-col items-center justify-start" *ngIf="isDividerLayout" matTooltip="">
<button mat-icon-button
type="button"
matTooltip="{{ 'layout.settings' | translate }}"
@ -162,25 +160,24 @@
(click)="openLayoutSettings('right')"
class="tb-layout-preview-element"
aria-label="Layout settings"
[ngClass]="layoutButtonClass('right')">
[class]="layoutButtonClass('right')">
<mat-icon>settings</mat-icon>
</button>
<button fxFlex
type="button"
<button type="button"
mat-raised-button
color="primary"
class="tb-layout-button tb-layout-button-right"
class="tb-layout-button tb-layout-button-right flex-1"
(mouseover)="rightLayoutTooltip.show()"
(mouseleave)="rightLayoutTooltip.hide()"
(click)="setFixedLayout('right')"
[ngClass]="layoutButtonClass('right')">
[class]="layoutButtonClass('right')">
<span [matTooltip]="layoutButtonText('right')"
#rightLayoutTooltip="matTooltip"
matTooltipPosition="above">
{{ 'layout.right' | translate }}
</span>
</button>
<div fxFlex class="tb-layout-preview-element tb-layout-preview-input" *ngIf="showPreviewInputs('right')">
<div class="tb-layout-preview-element tb-layout-preview-input flex-1" *ngIf="showPreviewInputs('right')">
<input *ngIf="layoutsFormGroup.get('type').value !== layoutWidthType.FIXED"
formControlName="rightWidthPercentage"
type="number"
@ -199,7 +196,7 @@
</div>
</div>
</div>
<div fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="8px" *ngIf="isDividerLayout" style="width: 80%">
<div class="flex flex-col items-center justify-center gap-2" *ngIf="isDividerLayout" style="width: 80%">
<mat-slider *ngIf="layoutsFormGroup.get('type').value === layoutWidthType.PERCENTAGE"
step="1"
min="{{ layoutPercentageSize.MIN }}"
@ -218,7 +215,7 @@
discrete
[displayWith]="formatSliderTooltipLabel.bind(this)"><input matSliderThumb formControlName="sliderFixed"/>
</mat-slider>
<div fxLayout="row" fxLayoutAlign="space-between center" class="tb-hint tb-hint-group" style="width: 100%;">
<div class="tb-hint tb-hint-group flex flex-row items-center justify-between" style="width: 100%;">
<label>
{{layoutsFormGroup.get('type').value === layoutWidthType.PERCENTAGE ? layoutPercentageSize.MIN + '%' : layoutFixedSize.MIN + 'px'}}
</label>
@ -230,7 +227,7 @@
</div>
</ng-template>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button
color="primary"
type="button"

6
ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.scss

@ -30,7 +30,6 @@ $tb-warn: mat.m2-get-color-from-palette(map-get($tb-theme, warn), text);
.tb-hint-group {
padding: 0;
margin-top: -14px;
display: block;
}
.tb-layout-preview {
@ -182,6 +181,11 @@ $tb-warn: mat.m2-get-color-from-palette(map-get($tb-theme, warn), text);
.mat-button-toggle {
border: none !important;
}
.mat-button-toggle-label-content {
.mat-pseudo-checkbox {
display: none;
}
}
}
}

6
ui-ngx/src/app/modules/home/components/dashboard-page/layout/move-widgets-dialog.component.html

@ -18,7 +18,7 @@
<form (ngSubmit)="move()">
<mat-toolbar color="primary">
<h2 translate>dashboard.move-all-widgets</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -30,7 +30,7 @@
<div mat-dialog-content class="tb-form-panel no-border no-padding" [formGroup]="moveWidgetsFormGroup">
<div class="tb-form-row space-between column-xs">
<div translate>dashboard.move-by</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div class="flex flex-row items-center justify-start gap-2">
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input required matInput formControlName="cols"
type="number" step="1" placeholder="{{ 'widget-config.set' | translate }}">
@ -44,7 +44,7 @@
</div>
</div>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

6
ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.html

@ -18,7 +18,7 @@
<form [formGroup]="stateFormGroup" (ngSubmit)="save()" style="min-width: 480px;">
<mat-toolbar color="primary">
<h2 translate>{{ isAdd ? 'dashboard.add-state' : 'dashboard.edit-state' }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -28,7 +28,7 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column">
<fieldset [disabled]="isLoading$ | async" class="flex flex-col">
<mat-form-field class="mat-block">
<mat-label translate>dashboard.state-name</mat-label>
<input required matInput formControlName="name">
@ -51,7 +51,7 @@
</mat-checkbox>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

2
ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<mat-select class="default-state-controller" [fxShow]="displayStateSelection()"
<mat-select class="default-state-controller" [class.!hidden]="!displayStateSelection()"
[(ngModel)]="stateObject[0].id" (ngModelChange)="selectedStateIdChanged()">
<mat-option *ngFor="let stateKv of states | keyvalue" [value]="stateKv.key">
{{getStateName(stateKv.key, stateKv.value)}}

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

@ -16,11 +16,12 @@
-->
<div class="entity-state-controller">
<div *ngIf="!isMobile || stateObject.length === 1" fxLayout="row" fxLayoutAlign="start center">
<span *ngFor="let state of stateObject; index as i; last as isLast" class="state-entry" [ngStyle]="{fontWeight: isLast ? 'bold' : 'normal',
cursor: isLast ? 'default' : 'pointer'}" (click)="navigatePrevState(i)">
<div *ngIf="!isMobile || stateObject.length === 1" class="flex flex-row items-center justify-start">
<span *ngFor="let state of stateObject; index as i; last as isLast" class="state-entry"
[style.font-weight]="isLast ? 'bold' : 'normal'"
[style.cursor]="isLast ? 'default' : 'pointer'" (click)="navigatePrevState(i)">
{{getStateName(i)}}
<span class='state-divider' [fxHide]="isLast"> > </span>
<span class='state-divider' [class.!hidden]="isLast"> > </span>
</span>
</div>
<mat-select *ngIf="isMobile && stateObject.length > 1"

23
ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html

@ -18,7 +18,7 @@
<form [formGroup]="statesFormGroup" (ngSubmit)="save()" style="width: 750px;">
<mat-toolbar color="primary">
<h2 translate>dashboard.manage-states</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -31,11 +31,11 @@
<fieldset [disabled]="isLoading$ | async">
<div class="manage-dashboard-states">
<div class="tb-entity-table">
<div fxLayout="column" class="tb-entity-table-content">
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="!textSearchMode">
<div class="tb-entity-table-content flex flex-col">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="textSearchMode">
<div class="mat-toolbar-tools">
<span class="tb-entity-table-title" translate>dashboard.states</span>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button [disabled]="isLoading$ | async"
type="button"
(click)="addState($event)"
@ -51,7 +51,7 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="textSearchMode">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="!textSearchMode">
<div class="mat-toolbar-tools">
<button mat-icon-button
type="button"
@ -59,7 +59,7 @@
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<mat-form-field fxFlex>
<mat-form-field class="flex-1">
<mat-label>&nbsp;</mat-label>
<input #searchInput matInput
[(ngModel)]="pageLink.textSearch"
@ -99,7 +99,7 @@
<mat-header-cell *matHeaderCellDef style="min-width: 120px; max-width: 120px; width: 120px">
</mat-header-cell>
<mat-cell *matCellDef="let state">
<div fxFlex fxLayout="row">
<div class="flex flex-1 flex-row">
<button mat-icon-button [disabled]="isLoading$ | async"
type="button"
matTooltip="{{ 'dashboard.edit-state' | translate }}"
@ -114,7 +114,7 @@
(click)="duplicateState($event, state)">
<mat-icon>content_copy</mat-icon>
</button>
<button [fxShow]="!state.root" mat-icon-button [disabled]="isLoading$ | async"
<button [class.!hidden]="state.root" mat-icon-button [disabled]="isLoading$ | async"
type="button"
matTooltip="{{ 'dashboard.delete-state' | translate }}"
matTooltipPosition="above"
@ -128,9 +128,8 @@
<mat-row class="mat-row-select"
*matRowDef="let state; columns: displayedColumns;"></mat-row>
</table>
<span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center"
class="no-data-found" translate>{{ 'dashboard.no-states-text' }}</span>
<span [class.!hidden]="(dataSource.isEmpty() | async) === false"
class="no-data-found flex items-center justify-center" translate>{{ 'dashboard.no-states-text' }}</span>
</div>
<mat-divider></mat-divider>
<mat-paginator [length]="dataSource.total() | async"
@ -142,7 +141,7 @@
</div>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

2
ui-ngx/src/app/modules/home/components/dashboard-page/widget-types-panel.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxLayout="column" class="mat-content mat-padding">
<div class="mat-content mat-padding flex flex-col">
<mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="type.display" *ngFor="let type of types"
(ngModelChange)="update()">
{{ 'widget.' + type.type | translate }}

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

@ -15,15 +15,15 @@
limitations under the License.
-->
<div fxFlex fxLayout="column" class="tb-progress-cover" fxLayoutAlign="center center"
<div class="tb-progress-cover flex flex-1 flex-col items-center justify-center"
[style]="dashboardStyle"
[style.backgroundImage]="backgroundImage"
[fxShow]="(isLoading$ | async) && !ignoreLoading && !isEdit">
[style.background-image]="backgroundImage"
[class.!hidden]="(isLoading$ | async) === false || ignoreLoading || isEdit">
<mat-spinner color="warn" mode="indeterminate" diameter="100">
</mat-spinner>
</div>
<div id="gridster-parent"
fxFlex class="tb-dashboard-content" [class.autofill-height]="isAutofillHeight()"
class="tb-dashboard-content flex-1" [class.autofill-height]="isAutofillHeight()"
[class.center-vertical]="centerVertical"
[class.center-horizontal]="centerHorizontal"
(mousedown)="onDashboardMouseDown($event)"

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

@ -30,3 +30,11 @@
}
}
}
:host ::ng-deep {
button.tb-layout-button {
.mdc-button__label {
line-height: 36px;
}
}
}

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

@ -18,7 +18,7 @@
<form>
<mat-toolbar color="primary">
<h2 translate>layout.select</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -28,19 +28,17 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="8px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<button fxFlex
mat-raised-button color="primary"
<fieldset [disabled]="isLoading$ | async" class="flex flex-col gap-2">
<div class="flex flex-row items-center justify-start gap-2">
<button mat-raised-button color="primary"
type="button"
class="tb-layout-button"
class="tb-layout-button flex-1"
(click)="selectLayout('main')">
<span translate>layout.main</span>
</button>
<button fxFlex
mat-raised-button color="primary"
<button mat-raised-button color="primary"
type="button"
class="tb-layout-button"
class="tb-layout-button flex-1"
(click)="selectLayout('right')">
<span translate>layout.right</span>
</button>

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

@ -18,7 +18,7 @@
<form [formGroup]="stateFormGroup" (ngSubmit)="save()">
<mat-toolbar color="primary">
<h2 translate>dashboard.select-state</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -28,7 +28,7 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column">
<fieldset [disabled]="isLoading$ | async" class="flex flex-col">
<mat-form-field class="mat-block">
<mat-label translate>dashboard.state</mat-label>
<mat-select required formControlName="stateId">
@ -39,7 +39,7 @@
</mat-form-field>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

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

@ -16,10 +16,10 @@
-->
<header>
<mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}">
<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" [fxHide]="showSearchPane">
<div class="tb-details-title" fxLayout="row" fxLayoutAlign="start center">
<mat-toolbar class="details-toolbar" color="primary" [style.height]="headerHeightPx+'px'">
<div class="mat-toolbar-tools flex flex-row items-center justify-start" style="height: 100%;">
<div class="mat-toolbar-tools tb-details-title-header flex flex-1 flex-col items-start justify-start" [class.!hidden]="showSearchPane">
<div class="tb-details-title flex flex-row items-center justify-start">
<ng-content select=".prefix-title-buttons"></ng-content>
<span class="tb-details-title-text">{{ headerTitle }}</span>
</div>
@ -28,7 +28,7 @@
<ng-content select=".header-pane"></ng-content>
</span>
</div>
<div *ngIf="isShowSearch" [fxHide]="!showSearchPane" fxLayout="row" fxLayoutAlign="start center" style="width: 100%;">
<div *ngIf="isShowSearch" [class.!hidden]="!showSearchPane" class="flex flex-row items-center justify-start" style="width: 100%;">
<button mat-icon-button (click)="onToggleSearch()">
<mat-icon class="material-icons">arrow_back</mat-icon>
</button>
@ -44,13 +44,13 @@
<mat-icon class="material-icons">close</mat-icon>
</button>
</div>
<section *ngIf="!isReadOnly" fxLayout="row" class="layout-wrap tb-header-buttons">
<section *ngIf="!isReadOnly" class="layout-wrap tb-header-buttons flex flex-row">
<button [disabled]="(isLoading$ | async) || theForm?.invalid || !theForm?.dirty"
mat-fab
matTooltip="{{ 'action.apply-changes' | translate }}"
matTooltipPosition="above"
color="accent" class="tb-btn-header mat-fab-bottom-right"
[ngClass]="{'tb-hide': !isEdit}"
[class.tb-hide]="!isEdit"
(click)="onApplyDetails()">
<mat-icon class="material-icons">done</mat-icon>
</button>
@ -65,6 +65,6 @@
</section>
</mat-toolbar>
</header>
<div fxFlex class="mat-content" [ngStyle]="{backgroundColor: backgroundColor}">
<div class="mat-content flex-1" [style.background-color]="backgroundColor">
<ng-content></ng-content>
</div>

2
ui-ngx/src/app/modules/home/components/device/copy-device-credentials.component.html

@ -19,7 +19,7 @@
ngxClipboard
[cbContent]="credential"
(cbOnSuccess)="onCopyCredential()"
[fxHide]="hideButton"
[class.!hidden]="hideButton"
[disabled]="disabled || loading">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span>{{ buttonLabel }}</span>

4
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<section [formGroup]="serverFormGroup" fxLayout="column">
<section [formGroup]="serverFormGroup" class="flex flex-col">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityMode">
@ -24,7 +24,7 @@
</mat-option>
</mat-select>
</mat-form-field>
<div [fxShow]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
<div [class.!hidden]="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.NO_SEC">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<mat-icon class="mat-primary" aria-hidden="false" aria-label="help-icon" matSuffix style="cursor:pointer;"

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

@ -16,7 +16,7 @@
-->
<section [formGroup]="deviceCredentialsFormGroup">
<section [fxShow]="credentialsTypes?.length > 1" style="margin-bottom: 16px">
<section [class.!hidden]="!(credentialsTypes?.length > 1)" style="margin-bottom: 16px">
<div class="tb-type-title" translate>device.credentials-type</div>
<tb-toggle-select formControlName="credentialsType" appearance="fill">
<tb-toggle-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">

6
ui-ngx/src/app/modules/home/components/device/device-info-filter.component.html

@ -30,9 +30,9 @@
</ng-container>
</ng-template>
<ng-template #deviceFilterPanel>
<form fxLayout="column" class="mat-content mat-padding" (ngSubmit)="update()">
<form class="mat-content mat-padding flex flex-col" (ngSubmit)="update()">
<ng-container *ngTemplateOutlet="deviceFilter"></ng-container>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<div class="tb-panel-actions flex flex-row items-center justify-end">
<button type="button"
mat-button
(click)="cancel()">
@ -48,7 +48,7 @@
</form>
</ng-template>
<ng-template #deviceFilter>
<div fxLayout="column" fxLayoutGap="16px" style="min-width: 280px;" [formGroup]="deviceInfoFilterForm">
<div class="flex flex-col gap-4" style="min-width: 280px;" [formGroup]="deviceInfoFilterForm">
<tb-device-profile-autocomplete
subscriptSizing="dynamic"
formControlName="deviceProfileId"

6
ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.html

@ -15,10 +15,10 @@
limitations under the License.
-->
<form (ngSubmit)="add()" style="min-width: 400px; width: 750px;" [ngStyle]="entitiesTableConfig.addDialogStyle">
<form (ngSubmit)="add()" style="min-width: 400px; width: 750px;" [style]="entitiesTableConfig.addDialogStyle">
<mat-toolbar color="primary">
<h2 translate>{{ translations.add }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<div [tb-help]="helpLinkId()"></div>
<button mat-icon-button
(click)="cancel()"
@ -32,7 +32,7 @@
<div mat-dialog-content>
<tb-anchor #entityDetailsForm></tb-anchor>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
cdkFocusInitial

149
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -34,18 +34,18 @@
</mat-drawer>
<mat-drawer-content>
<div class="mat-padding tb-entity-table tb-absolute-fill">
<div fxLayout="column" class="tb-entity-table-content tb-outlined-border">
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="!textSearchMode && dataSource.selection.isEmpty()">
<div class="tb-entity-table-content tb-outlined-border flex flex-col">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="textSearchMode || !dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<div class="title-container flex flex-row items-center justify-start xs:flex-col xs:items-start xs:justify-center">
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>
<tb-anchor #entityTableHeader></tb-anchor>
<tb-timewindow *ngIf="entitiesTableConfig.useTimePageLink" [(ngModel)]="timewindow"
(ngModelChange)="onTimewindowChange()"
asButton strokedButton historyOnly [forAllTimeEnabled]="entitiesTableConfig.forAllTimeEnabled"></tb-timewindow>
</div>
<span fxFlex></span>
<div [fxShow]="addEnabled()">
<span class="flex-1"></span>
<div [class.!hidden]="!addEnabled()">
<button mat-icon-button [disabled]="isLoading$ | async"
*ngIf="!entitiesTableConfig.addActionDescriptors.length; else addActions"
(click)="addEntity($event)"
@ -56,7 +56,7 @@
<ng-template #addActions>
<button mat-icon-button [disabled]="isLoading$ | async"
*ngIf="entitiesTableConfig.addActionDescriptors.length === 1; else addActionsMenu"
[fxShow]="entitiesTableConfig.addActionDescriptors[0].isEnabled()"
[class.!hidden]="!entitiesTableConfig.addActionDescriptors[0].isEnabled()"
(click)="entitiesTableConfig.addActionDescriptors[0].onAction($event)"
matTooltip="{{ entitiesTableConfig.addActionDescriptors[0].name }}"
matTooltipPosition="above">
@ -72,7 +72,7 @@
<mat-menu #addActionsMenu="matMenu" xPosition="before">
<button mat-menu-item *ngFor="let actionDescriptor of entitiesTableConfig.addActionDescriptors"
[disabled]="isLoading$ | async"
[fxShow]="actionDescriptor.isEnabled()"
[class.!hidden]="!actionDescriptor.isEnabled()"
(click)="actionDescriptor.onAction($event)">
<tb-icon matMenuItemIcon>{{actionDescriptor.icon}}</tb-icon>
<span>{{ actionDescriptor.name }}</span>
@ -82,7 +82,7 @@
</ng-template>
</div>
<button mat-icon-button [disabled]="isLoading$ | async"
[fxShow]="actionDescriptor.isEnabled()" *ngFor="let actionDescriptor of headerActionDescriptors"
[class.!hidden]="!actionDescriptor.isEnabled()" *ngFor="let actionDescriptor of headerActionDescriptors"
matTooltip="{{ actionDescriptor.name }}"
matTooltipPosition="above"
(click)="actionDescriptor.onAction($event)">
@ -101,14 +101,14 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar class="mat-mdc-table-toolbar" [fxShow]="textSearchMode && dataSource.selection.isEmpty()">
<mat-toolbar class="mat-mdc-table-toolbar" [class.!hidden]="!textSearchMode || !dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<button mat-icon-button
matTooltip="{{ translations.search | translate }}"
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<mat-form-field fxFlex>
<mat-form-field class="flex-1">
<mat-label>&nbsp;</mat-label>
<input #searchInput matInput
[formControl]="textSearch"
@ -121,14 +121,14 @@
</button>
</div>
</mat-toolbar>
<mat-toolbar *ngIf="entitiesTableConfig.selectionEnabled" class="mat-mdc-table-toolbar" color="primary" [fxShow]="!dataSource.selection.isEmpty()">
<mat-toolbar *ngIf="entitiesTableConfig.selectionEnabled" class="mat-mdc-table-toolbar" color="primary" [class.!hidden]="dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools">
<span class="tb-entity-table-info">
{{ translations.selectedEntities | translate:{count: dataSource.selection.selected.length} }}
</span>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button [disabled]="isLoading$ | async"
[fxShow]="actionDescriptor.isEnabled" *ngFor="let actionDescriptor of groupActionDescriptors"
[class.!hidden]="!actionDescriptor.isEnabled" *ngFor="let actionDescriptor of groupActionDescriptors"
matTooltip="{{ actionDescriptor.name }}"
matTooltipPosition="above"
(click)="actionDescriptor.onAction($event, dataSource.selection.selected)">
@ -136,7 +136,7 @@
</button>
</div>
</mat-toolbar>
<div fxFlex class="table-container">
<div class="table-container flex-1">
<table mat-table [dataSource]="dataSource" [trackBy]="trackByEntityId"
matSort [matSortActive]="pageLink.sortOrder?.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="select" sticky>
@ -148,23 +148,23 @@
</mat-header-cell>
<mat-cell *matCellDef="let entity">
<mat-checkbox (click)="$event.stopPropagation()"
[fxShow]="entitiesTableConfig.entitySelectionEnabled(entity)"
[class.!hidden]="!entitiesTableConfig.entitySelectionEnabled(entity)"
(change)="$event ? dataSource.selection.toggle(entity) : null"
[checked]="dataSource.selection.isSelected(entity)">
</mat-checkbox>
</mat-cell>
</ng-container>
<ng-container [matColumnDef]="column.key" *ngFor="let column of entityColumns; trackBy: trackByColumnKey;">
<mat-header-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
[fxHide.lt-lg]="column.mobileHide"
*matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable">
<mat-header-cell [class.mat-number-cell]="column.isNumberColumn"
[class.lt-lg:!hidden]="column.mobileHide"
*matHeaderCellDef [style]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable">
{{ column.ignoreTranslate ? column.title : (column.title | translate) }} </mat-header-cell>
<mat-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
[fxHide.lt-lg]="column.mobileHide"
<mat-cell [class.mat-number-cell]="column.isNumberColumn"
[class.lt-lg:!hidden]="column.mobileHide"
*matCellDef="let entity; let row = index"
[matTooltip]="cellTooltip(entity, column, row)"
matTooltipPosition="above"
[ngStyle]="cellStyle(entity, column, row)">
[style]="cellStyle(entity, column, row)">
<ng-container [ngSwitch]="column.type">
<ng-template [ngSwitchCase]="'link'">
<a *ngIf="column.entityURL(entity) as detailsPageURL; else defaultContent"
@ -183,7 +183,7 @@
<ng-template [ngSwitchCase]="cellActionType.COPY_BUTTON">
<tb-copy-button
[disabled]="isLoading$ | async"
[fxShow]="column.actionCell.isEnabled(entity)"
[class.!hidden]="!column.actionCell.isEnabled(entity)"
[copyText]="column.actionCell.onAction(null, entity)"
tooltipText="{{ column.actionCell.nameFunction ? column.actionCell.nameFunction(entity) : column.actionCell.name }}"
tooltipPosition="above"
@ -193,11 +193,11 @@
</ng-template>
<ng-template ngSwitchDefault>
<button mat-icon-button [disabled]="isLoading$ | async"
[fxShow]="column.actionCell.isEnabled(entity)"
[class.!hidden]="!column.actionCell.isEnabled(entity)"
matTooltip="{{ column.actionCell.nameFunction ? column.actionCell.nameFunction(entity) : column.actionCell.name }}"
matTooltipPosition="above"
(click)="column.actionCell.onAction($event, entity)">
<tb-icon [ngStyle]="column.actionCell.style">
<tb-icon [style]="column.actionCell.style">
{{column.actionCell.icon}}
</tb-icon>
</button>
@ -207,74 +207,79 @@
</mat-cell>
</ng-container>
<ng-container [matColumnDef]="column.key" *ngFor="let column of actionColumns; trackBy: trackByColumnKey;">
<mat-header-cell *matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable"> {{ column.title | translate }} </mat-header-cell>
<mat-header-cell *matHeaderCellDef [style]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable"> {{ column.title | translate }} </mat-header-cell>
<mat-cell *matCellDef="let entity; let row = index"
[ngStyle]="cellStyle(entity, column, row)">
[style]="cellStyle(entity, column, row)">
<button mat-icon-button [disabled]="isLoading$ | async"
[fxShow]="column.actionDescriptor.isEnabled(entity)"
[class.!hidden]="!column.actionDescriptor.isEnabled(entity)"
matTooltip="{{ column.actionDescriptor.nameFunction ? column.actionDescriptor.nameFunction(entity) : column.actionDescriptor.name }}"
matTooltipPosition="above"
(click)="column.actionDescriptor.onAction($event, entity)">
<tb-icon [ngStyle]="column.actionDescriptor.style">
<tb-icon [style]="column.actionDescriptor.style">
{{column.actionDescriptor.iconFunction ? column.actionDescriptor.iconFunction(entity) : column.actionDescriptor.icon}}
</tb-icon>
</button>
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (cellActionDescriptors.length * 48) + 'px',
maxWidth: (cellActionDescriptors.length * 48) + 'px',
width: (cellActionDescriptors.length * 48) + 'px' }">
{{ entitiesTableConfig.actionsColumnTitle ? (entitiesTableConfig.actionsColumnTitle | translate) : '' }}
<mat-header-cell *matHeaderCellDef>
<ng-container *ngIf="cellActionDescriptors.length">
<div class="gt-md:!hidden" style="min-width: 48px;">
{{ entitiesTableConfig.actionsColumnTitle ? (entitiesTableConfig.actionsColumnTitle | translate) : '' }}
</div>
<div class="lt-lg:!hidden"
[style.min-width]="(cellActionDescriptors.length * 48) + 'px'">
{{ entitiesTableConfig.actionsColumnTitle ? (entitiesTableConfig.actionsColumnTitle | translate) : '' }}
</div>
</ng-container>
</mat-header-cell>
<mat-cell *matCellDef="let entity" [ngStyle.gt-md]="{ minWidth: (cellActionDescriptors.length * 48) + 'px',
maxWidth: (cellActionDescriptors.length * 48) + 'px',
width: (cellActionDescriptors.length * 48) + 'px' }">
<div [fxHide]="cellActionDescriptors.length !== 1" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">
<button mat-icon-button [disabled]="(isLoading$ | async) || !actionDescriptor.isEnabled(entity)"
*ngFor="let actionDescriptor of cellActionDescriptors"
matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}"
matTooltipPosition="above"
(click)="actionDescriptor.onAction($event, entity)">
<tb-icon [ngStyle]="actionDescriptor.style">
{{actionDescriptor.iconFunction ? actionDescriptor.iconFunction(entity) : actionDescriptor.icon}}
</tb-icon>
</button>
</div>
<div fxHide [fxShow.lt-lg]="cellActionDescriptors.length !== 1" *ngIf="cellActionDescriptors.length">
<button mat-icon-button
(click)="$event.stopPropagation()"
[matMenuTriggerFor]="cellActionsMenu">
<mat-icon class="material-icons">more_vert</mat-icon>
</button>
<mat-menu #cellActionsMenu="matMenu" xPosition="before">
<button mat-menu-item *ngFor="let actionDescriptor of cellActionDescriptors"
[disabled]="isLoading$ | async"
[fxShow]="actionDescriptor.isEnabled(entity)"
<mat-cell *matCellDef="let entity">
<ng-container *ngIf="cellActionDescriptors.length">
<div [class.lt-lg:!hidden]="cellActionDescriptors.length !== 1" class="flex flex-row items-stretch justify-end"
[style.min-width]="(cellActionDescriptors.length * 48) + 'px'">
<button mat-icon-button [disabled]="(isLoading$ | async) || !actionDescriptor.isEnabled(entity)"
*ngFor="let actionDescriptor of cellActionDescriptors"
matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}"
matTooltipPosition="above"
(click)="actionDescriptor.onAction($event, entity)">
<tb-icon matMenuItemIcon [ngStyle]="actionDescriptor.style">
<tb-icon [style]="actionDescriptor.style">
{{actionDescriptor.iconFunction ? actionDescriptor.iconFunction(entity) : actionDescriptor.icon}}
</tb-icon>
<span>{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}</span>
</button>
</mat-menu>
</div>
</div>
<div [class.!hidden]="cellActionDescriptors.length === 1" class="gt-md:!hidden">
<button mat-icon-button
(click)="$event.stopPropagation()"
[matMenuTriggerFor]="cellActionsMenu">
<mat-icon class="material-icons">more_vert</mat-icon>
</button>
<mat-menu #cellActionsMenu="matMenu" xPosition="before">
<button mat-menu-item *ngFor="let actionDescriptor of cellActionDescriptors"
[disabled]="isLoading$ | async"
[class.!hidden]="!actionDescriptor.isEnabled(entity)"
(click)="actionDescriptor.onAction($event, entity)">
<tb-icon matMenuItemIcon [style]="actionDescriptor.style">
{{actionDescriptor.iconFunction ? actionDescriptor.iconFunction(entity) : actionDescriptor.icon}}
</tb-icon>
<span>{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}</span>
</button>
</mat-menu>
</div>
</ng-container>
</mat-cell>
</ng-container>
<mat-header-row [ngClass]="{'mat-row-select': selectionEnabled}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [fxShow]="!dataSource.dataLoading"
[ngClass]="{'mat-row-select': selectionEnabled,
'mat-selected': dataSource.selection.isSelected(entity),
'tb-current-entity': dataSource.isCurrentEntity(entity),
'tb-pointer': entitiesTableConfig.rowPointer}"
<mat-header-row [class.mat-row-select]="selectionEnabled" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [class.!hidden]="dataSource.dataLoading"
[class.mat-row-select]="selectionEnabled"
[class.mat-selected]="dataSource.selection.isSelected(entity)"
[class.tb-current-entity]="dataSource.isCurrentEntity(entity)"
[class.tb-pointer]="entitiesTableConfig.rowPointer"
*matRowDef="let entity; columns: displayedColumns;" (click)="onRowClick($event, entity)"></mat-row>
</table>
<span [fxShow]="!(isLoading$ | async) && (dataSource.isEmpty() | async) && !dataSource.dataLoading"
fxLayoutAlign="center center"
class="no-data-found">{{ translations.noEntities | translate }}</span>
<span [fxShow]="dataSource.dataLoading"
fxLayoutAlign="center center"
class="no-data-found">{{ 'common.loading' | translate }}</span>
<span [class.!hidden]="(isLoading$ | async) || (dataSource.isEmpty() | async) === false || dataSource.dataLoading"
class="no-data-found flex items-center justify-center">{{ translations.noEntities | translate }}</span>
<span [class.!hidden]="!dataSource.dataLoading"
class="no-data-found flex items-center justify-center">{{ 'common.loading' | translate }}</span>
</div>
<mat-divider *ngIf="displayPagination"></mat-divider>
<mat-paginator *ngIf="displayPagination"

14
ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html

@ -17,26 +17,26 @@
-->
<mat-card appearance="outlined" class="settings-card">
<mat-toolbar class="details-toolbar">
<div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div class="mat-toolbar-tools flex flex-row items-center justify-start gap-2">
<button mat-icon-button
matTooltip="{{ 'action.back' | translate }}"
matTooltipPosition="above"
(click)="goBack()">
<mat-icon>arrow_back</mat-icon>
</button>
<div class="tb-details-title-header" fxLayout="column" fxLayoutAlign="center start">
<div class="tb-details-title-header flex flex-col items-start justify-center">
<div class="tb-details-title tb-ellipsis">{{ headerTitle }}</div>
<div class="tb-details-subtitle tb-ellipsis">{{ headerSubtitle }}</div>
</div>
<div class="tb-help" [tb-help]="helpLinkId()"></div>
<span fxFlex></span>
<section *ngIf="!isReadOnly" fxLayout="row" class="tb-header-button" fxLayoutGap="8px">
<span class="flex-1"></span>
<section *ngIf="!isReadOnly" class="tb-header-button flex flex-row gap-2">
<button [disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty"
mat-fab
matTooltip="{{ 'action.apply-changes' | translate }}"
matTooltipPosition="above"
color="accent" class="tb-btn-header"
[ngClass]="{'tb-hide': !isEdit}"
[class.tb-hide]="!isEdit"
(click)="onApplyDetails()">
<mat-icon class="material-icons">done</mat-icon>
</button>
@ -51,8 +51,8 @@
</section>
</div>
</mat-toolbar>
<mat-card-content fxFlex="100">
<mat-tab-group mat-stretch-tabs="false" class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" [(selectedIndex)]="selectedTab" fxFill>
<mat-card-content class="flex-full">
<mat-tab-group mat-stretch-tabs="false" class="tb-absolute-fill size-full" [class.tb-headless]="hideDetailsTabs()" [(selectedIndex)]="selectedTab">
<mat-tab label="{{ 'details.details' | translate }}">
<tb-anchor #entityDetailsForm></tb-anchor>
</mat-tab>

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

@ -15,7 +15,7 @@
limitations under the License.
-->
<tb-details-panel fxFlex
<tb-details-panel class="flex-1"
[headerTitle]="entitiesTableConfig.entityTitle(entity)"
headerSubtitle="{{ translations.details | translate }}"
[isReadOnly]="entitiesTableConfig.detailsReadonly(entity)"
@ -27,7 +27,7 @@
<div class="details-buttons">
<div [tb-help]="helpLinkId()"></div>
</div>
<mat-tab-group mat-stretch-tabs="false" class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" fxFlex [(selectedIndex)]="selectedTab">
<mat-tab-group mat-stretch-tabs="false" class="tb-absolute-fill flex-1" [class.tb-headless]="hideDetailsTabs()" [(selectedIndex)]="selectedTab">
<mat-tab label="{{ 'details.details' | translate }}">
<tb-anchor #entityDetailsForm></tb-anchor>
</mat-tab>

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

@ -15,10 +15,10 @@
limitations under the License.
-->
<div fxLayout="column" class="tb-entity-filter-view">
<div class="tb-entity-filter-view flex flex-col">
<div *ngIf="!filter || !filter.type; else filterView" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div>
<ng-template #filterView>
<div fxLayout="column">
<div class="flex flex-col">
<div class="entity-filter-value">{{ filterDisplayValue }}</div>
</div>
</ng-template>

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

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxLayout="column" [formGroup]="entityFilterFormGroup" class="tb-entity-filter">
<div [formGroup]="entityFilterFormGroup" class="tb-entity-filter flex flex-col">
<mat-form-field class="mat-block">
<mat-label translate>alias.filter-type</mat-label>
<mat-select required formControlName="type">
@ -27,7 +27,7 @@
{{ 'alias.filter-type-required' | translate }}
</mat-error>
</mat-form-field>
<section fxLayout="column" [formGroup]="filterFormGroup" [ngSwitch]="entityFilterFormGroup.get('type').value">
<section class="flex flex-col" [formGroup]="filterFormGroup" [ngSwitch]="entityFilterFormGroup.get('type').value">
<ng-template [ngSwitchCase]="aliasFilterType.singleEntity">
<tb-entity-select required
useAliasEntityTypes="true"
@ -78,9 +78,9 @@
<input matInput formControlName="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}">
</mat-form-field>
<div fxLayout="column">
<div class="flex flex-col">
<label class="tb-small">{{ 'alias.default-state-entity' | translate }}</label>
<tb-entity-select fxFlex
<tb-entity-select class="flex-1"
useAliasEntityTypes="true"
formControlName="defaultStateEntity">
</tb-entity-select>
@ -192,42 +192,42 @@
entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ?
entityFilterFormGroup.get('type').value : ''">
<label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
<section class="tb-root-state-entity-switch flex flex-row items-center justify-start" style="padding-left: 0;">
<mat-slide-toggle class="root-state-entity-switch"
formControlName="rootStateEntity">
</mat-slide-toggle>
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
</section>
<div fxFlex fxLayout="row" *ngIf="!filterFormGroup.get('rootStateEntity').value">
<tb-entity-select fxFlex
<div class="flex flex-1 flex-row" *ngIf="!filterFormGroup.get('rootStateEntity').value">
<tb-entity-select class="flex-1"
required
useAliasEntityTypes="true"
formControlName="rootEntity">
</tb-entity-select>
</div>
<div fxFlex fxLayout="column" fxLayout.gt-sm="row" *ngIf="filterFormGroup.get('rootStateEntity').value">
<div class="flex flex-1 flex-col gt-sm:flex-row" *ngIf="filterFormGroup.get('rootStateEntity').value">
<mat-form-field floatLabel="always" class="mat-block" style="margin-top: 24px; padding-right: 8px;">
<mat-label translate>alias.state-entity-parameter-name</mat-label>
<input matInput formControlName="stateEntityParamName"
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}">
</mat-form-field>
<div fxFlex fxLayout="column">
<div class="flex flex-1 flex-col">
<label class="tb-small">{{ 'alias.default-state-entity' | translate }}</label>
<tb-entity-select fxFlex
<tb-entity-select class="flex-1"
useAliasEntityTypes="true"
formControlName="defaultStateEntity">
</tb-entity-select>
</div>
</div>
<div fxFlex fxLayout="row">
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
<div class="flex flex-1 flex-row">
<section class="tb-root-state-entity-switch flex flex-row items-center justify-start" style="padding-left: 0;">
<mat-slide-toggle class="root-state-entity-switch"
formControlName="fetchLastLevelOnly">
</mat-slide-toggle>
<label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label>
</section>
</div>
<div fxFlex fxLayoutGap="8px" fxLayout="row" fxLayout.xs="column">
<div class="flex flex-1 flex-row gap-2 xs:flex-col">
<mat-form-field class="mat-block" style="min-width: 100px;">
<mat-label translate>relation.direction</mat-label>
<mat-select required formControlName="direction">
@ -236,7 +236,7 @@
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex floatLabel="always" class="mat-block">
<mat-form-field floatLabel="always" class="mat-block flex-1">
<mat-label translate>alias.max-relation-level</mat-label>
<input matInput
type="number"
@ -248,7 +248,7 @@
</div>
<div class="mat-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div>
<tb-relation-type-autocomplete
fxFlex
class="flex-1"
formControlName="relationType">
</tb-relation-type-autocomplete>
</ng-template>

10
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html

@ -15,21 +15,21 @@
limitations under the License.
-->
<mat-toolbar fxLayout="row" color="primary">
<mat-toolbar class="flex flex-row" color="primary">
<h2 translate>{{ title }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
[mat-dialog-close]="false"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content fxLayout="column">
<div mat-dialog-content class="flex flex-col">
<div #eventContentEditor class="tb-event-content">
</div>
</div>
<div mat-dialog-actions fxLayout="row">
<span fxFlex></span>
<div mat-dialog-actions class="flex flex-row">
<span class="flex-1"></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

8
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html

@ -15,8 +15,8 @@
limitations under the License.
-->
<form fxLayout="column" class="tb-filter" [formGroup]="eventFilterFormGroup" (ngSubmit)="update()">
<div fxLayout="column" class="tb-filter-container mat-padding">
<form class="tb-filter flex flex-col" [formGroup]="eventFilterFormGroup" (ngSubmit)="update()">
<div class="tb-filter-container mat-padding flex flex-col">
<ng-container *ngFor="let column of showColumns">
<ng-container [ngSwitch]="column.key">
<ng-template [ngSwitchCase]="isSelector(column.key)">
@ -46,7 +46,7 @@
</tb-checkbox>
</ng-template>
<ng-template [ngSwitchCase]="'errorStr'">
<mat-form-field fxHide [fxShow]="showErrorMsgFields()">
<mat-form-field [class.!hidden]="!showErrorMsgFields()">
<mat-label>{{ column.title | translate}}</mat-label>
<input matInput type="text" name="errorSearchText" formControlName="errorStr">
</mat-form-field>
@ -60,7 +60,7 @@
</ng-container>
</ng-container>
</div>
<div fxLayout="row" class="tb-panel-actions mat-padding" fxLayoutAlign="end center">
<div class="tb-panel-actions mat-padding flex flex-row items-center justify-end">
<button type="button"
mat-button
(click)="cancel()">

6
ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html

@ -15,8 +15,8 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup">
<mat-form-field hideRequiredMarker fxFlex="30" class="mat-block">
<div class="flex flex-1 flex-row items-start justify-start gap-2" [formGroup]="booleanFilterPredicateFormGroup">
<mat-form-field hideRequiredMarker class="mat-block max-w-30% flex-full">
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
<mat-option *ngFor="let operation of booleanOperations" [value]="operation">
{{booleanOperationTranslations.get(booleanOperationEnum[operation]) | translate}}
@ -25,7 +25,7 @@
</mat-form-field>
<tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
[onlyUserDynamicSource]="onlyUserDynamicSource"
fxFlex="70"
class="max-w-70% flex-full"
[valueType]="valueTypeEnum.BOOLEAN"
formControlName="value">
</tb-filter-predicate-value>

6
ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html

@ -18,7 +18,7 @@
<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 1200px;">
<mat-toolbar color="primary">
<h2 translate>filter.complex-filter</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -26,7 +26,7 @@
</button>
</mat-toolbar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column">
<fieldset [disabled]="isLoading$ | async" class="flex flex-col">
<mat-form-field class="mat-block">
<mat-label translate>filter.operation.operation</mat-label>
<mat-select required formControlName="operation">
@ -46,7 +46,7 @@
</tb-filter-predicate-list>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

2
ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div class="flex flex-1 flex-row items-center justify-start gap-2">
<mat-label translate>filter.complex-filter</mat-label>
<button mat-icon-button color="primary"
class="tb-mat-32"

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

@ -18,7 +18,7 @@
<form [formGroup]="filterFormGroup" (ngSubmit)="save()" style="width: 700px;">
<mat-toolbar color="primary">
<h2>{{ (isAdd ? 'filter.add' : 'filter.edit') | translate }}</h2>
<span fxFlex></span>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -29,9 +29,9 @@
</mat-progress-bar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async">
<div fxFlex fxLayout="column">
<div fxLayout="row">
<mat-form-field fxFlex class="mat-block">
<div class="flex flex-1 flex-col">
<div class="flex flex-row">
<mat-form-field class="mat-block flex-1">
<mat-label translate>filter.name</mat-label>
<input matInput formControlName="filter" required>
<mat-error *ngIf="filterFormGroup.get('filter').hasError('required')">
@ -41,7 +41,7 @@
{{ 'filter.duplicate-filter' | translate }}
</mat-error>
</mat-form-field>
<section class="tb-editable-switch" fxLayout="column" fxLayoutAlign="start center">
<section class="tb-editable-switch flex flex-col items-center justify-start">
<label class="tb-small editable-label" translate>filter.editable</label>
<mat-slide-toggle class="editable-switch"
formControlName="editable">
@ -54,7 +54,7 @@
</div>
</fieldset>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"

42
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html

@ -15,41 +15,41 @@
limitations under the License.
-->
<section fxLayout="column" [formGroup]="filterListFormGroup">
<section class="flex flex-col" [formGroup]="filterListFormGroup">
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>filter.filters</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="row">
<span fxFlex="8"></span>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" fxFlex="92">
<div fxFlex fxLayout="row" fxLayoutGap="8px">
<div fxFlex="30" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<label fxFlex translate class="tb-title no-padding">filter.operation.operation</label>
<div class="flex flex-row">
<span class="max-w-8% flex-full"></span>
<div class="flex max-w-92% flex-full flex-row items-center justify-start gap-2">
<div class="flex flex-1 flex-row gap-2">
<div class="flex max-w-30% flex-full flex-row items-center justify-start gap-2">
<label translate class="tb-title no-padding flex-1">filter.operation.operation</label>
<label *ngIf="valueType === valueTypeEnum.STRING"
translate class="tb-title no-padding" style="min-width: 70px;">filter.ignore-case</label>
</div>
<label fxFlex="70" translate class="tb-title no-padding">filter.value</label>
<label translate class="tb-title no-padding max-w-70% flex-full">filter.value</label>
</div>
<label *ngIf="displayUserParameters"
translate class="tb-title no-padding" style="width: 60px;">filter.user-parameters</label>
<span [fxShow]="!disabled" style="min-width: 40px;">&nbsp;</span>
<span [class.!hidden]="disabled" style="min-width: 40px;">&nbsp;</span>
</div>
</div>
<mat-divider style="padding-bottom: 5px;"></mat-divider>
<div class="predicate-list">
<div fxLayout="row" fxLayoutAlign="start" style="height: 76px;"
<div class="flex flex-row items-stretch justify-start" style="height: 76px;"
formArrayName="predicates"
*ngFor="let predicateControl of predicatesFormArray.controls; let $index = index">
<div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation">
<div class="filters-operation flex max-w-8% flex-full flex-row items-stretch justify-center">
<span *ngIf="$index > 0">{{ complexOperationTranslations.get(operation) | translate }}</span>
</div>
<div fxLayout="column" fxFlex="92">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" fxFlex>
<div class="flex max-w-92% flex-full flex-col">
<div class="flex flex-1 flex-row items-center justify-start gap-2">
<tb-filter-predicate
fxFlex
class="flex-1"
[valueType]="valueType"
[displayUserParameters]="displayUserParameters"
[allowUserDynamicSource]="allowUserDynamicSource"
@ -58,7 +58,7 @@
[formControl]="predicateControl">
</tb-filter-predicate>
<button mat-icon-button color="primary"
[fxShow]="!disabled"
[class.!hidden]="disabled"
type="button"
(click)="removePredicate($index)"
matTooltip="{{ 'filter.remove-filter' | translate }}"
@ -68,13 +68,13 @@
</div>
</div>
</div>
<span [fxShow]="!predicatesFormArray.length"
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
class="no-data-found" translate>filter.no-filters</span>
<span [class.!hidden]="!!predicatesFormArray.length"
[class.disabled]="disabled"
class="no-data-found flex items-center justify-center" translate>filter.no-filters</span>
</div>
<div style="margin-top: 16px;" fxLayout="row" fxLayoutGap="8px">
<div style="margin-top: 16px;" class="flex flex-row gap-2">
<button mat-button mat-raised-button color="primary"
[fxShow]="!disabled"
[class.!hidden]="disabled"
(click)="addPredicate(false)"
type="button"
matTooltip="{{ 'filter.add-filter' | translate }}"
@ -82,7 +82,7 @@
{{ 'action.add' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
[fxShow]="!disabled"
[class.!hidden]="disabled"
(click)="addPredicate(true)"
type="button"
matTooltip="{{ 'filter.add-complex-filter' | translate }}"

16
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html

@ -15,9 +15,9 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup">
<div fxFlex fxLayout="column" [fxShow]="!dynamicMode && !onlyUserDynamicSource">
<div fxLayout="column" [ngSwitch]="valueType">
<div class="flex flex-1 flex-row items-center justify-start gap-2" [formGroup]="filterPredicateValueFormGroup">
<div class="flex flex-1 flex-col" [class.!hidden]="dynamicMode || onlyUserDynamicSource">
<div class="flex flex-col" [ngSwitch]="valueType">
<ng-template [ngSwitchCase]="valueTypeEnum.STRING">
<mat-form-field hideRequiredMarker class="mat-block">
<input matInput formControlName="defaultValue" placeholder="{{'filter.value' | translate}}">
@ -43,9 +43,9 @@
</div>
<div class="tb-hint">{{ hintText | translate }}</div>
</div>
<div fxFlex fxLayout="column" [fxShow]="dynamicMode || onlyUserDynamicSource">
<div formGroupName="dynamicValue" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div fxFlex="35" fxLayout="column">
<div class="flex flex-col" [class.!hidden]="!dynamicMode && !onlyUserDynamicSource">
<div formGroupName="dynamicValue" class="flex flex-row items-center justify-start gap-2">
<div class="flex max-w-35% flex-full flex-col">
<mat-form-field hideRequiredMarker class="mat-block">
<mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}">
<mat-option [value]="null">
@ -58,14 +58,14 @@
</mat-form-field>
<div class="tb-hint" translate>filter.dynamic-source-type</div>
</div>
<div fxFlex fxLayout="column">
<div class="flex flex-1 flex-col">
<mat-form-field hideRequiredMarker class="mat-block">
<input matInput formControlName="sourceAttribute" placeholder="{{'filter.source-attribute' | translate}}">
</mat-form-field>
<div class="tb-hint" translate>filter.source-attribute</div>
</div>
<div *ngIf="!allow && inheritMode"
fxLayout="column"
class="flex flex-col"
style="padding-top: 6px">
<mat-checkbox formControlName="inherit">
{{ 'filter.inherit-owner' | translate}}

6
ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html

@ -15,8 +15,8 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateFormGroup">
<div fxFlex fxLayout="column" [ngSwitch]="type">
<div class="flex flex-1 flex-row items-center justify-start gap-2" [formGroup]="filterPredicateFormGroup">
<div class="flex flex-1 flex-col" [ngSwitch]="type">
<ng-template [ngSwitchCase]="filterPredicateType.STRING">
<tb-string-filter-predicate
[allowUserDynamicSource]="allowUserDynamicSource"
@ -52,7 +52,7 @@
</div>
<tb-filter-user-info *ngIf="type !== filterPredicateType.COMPLEX && displayUserParameters"
style="width: 60px;"
fxLayout="row" fxLayoutAlign="center"
class="flex flex-row items-stretch justify-center"
[valueType]="valueType"
[operation]="filterPredicateFormGroup.get('predicate').value?.operation"
[key]="key"

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

Loading…
Cancel
Save