diff --git a/application/src/main/data/json/system/widget_bundles/edge_widgets.json b/application/src/main/data/json/system/widget_bundles/edge_widgets.json new file mode 100644 index 0000000000..553189ac6d --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/edge_widgets.json @@ -0,0 +1,25 @@ +{ + "widgetsBundle": { + "alias": "edge_widgets", + "title": "Edge widgets", + "image": null + }, + "widgetTypes": [ + { + "alias": "edges_overview", + "name": "Edges Quick Overview", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edges Quick Overview\",\"showTitleIcon\":true,\"titleIcon\":\"router\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" + } + } + ] +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index c9f6b94922..91a3912fbe 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -176,6 +176,7 @@ public class ThingsboardInstallService { systemDataLoaderService.deleteSystemWidgetBundle("input_widgets"); systemDataLoaderService.deleteSystemWidgetBundle("date"); systemDataLoaderService.deleteSystemWidgetBundle("entity_admin_widgets"); + systemDataLoaderService.deleteSystemWidgetBundle("edge_widgets"); systemDataLoaderService.loadSystemWidgets(); break; diff --git a/ui/src/app/api/component-descriptor.service.js b/ui/src/app/api/component-descriptor.service.js index b4cacf4991..b4398cb569 100644 --- a/ui/src/app/api/component-descriptor.service.js +++ b/ui/src/app/api/component-descriptor.service.js @@ -21,38 +21,63 @@ function ComponentDescriptorService($http, $q) { var componentsByType = {}; var componentsByClazz = {}; + var actionsByPlugin = {}; var service = { + getComponentDescriptorsByType: getComponentDescriptorsByType, + getComponentDescriptorByClazz: getComponentDescriptorByClazz, + getPluginActionsByPluginClazz: getPluginActionsByPluginClazz, getComponentDescriptorsByTypes: getComponentDescriptorsByTypes } return service; - function getComponentDescriptorsByTypes(componentTypes, ruleChainType) { + function getComponentDescriptorsByType(componentType) { + var deferred = $q.defer(); + if (componentsByType[componentType]) { + deferred.resolve(componentsByType[componentType]); + } else { + var url = '/api/components/' + componentType; + $http.get(url, null).then(function success(response) { + componentsByType[componentType] = response.data; + for (var i = 0; i < componentsByType[componentType].length; i++) { + var component = componentsByType[componentType][i]; + componentsByClazz[component.clazz] = component; + } + deferred.resolve(componentsByType[componentType]); + }, function fail() { + deferred.reject(); + }); + + } + return deferred.promise; + } + + function getComponentDescriptorsByTypes(componentTypes, type) { var deferred = $q.defer(); var result = []; - if (!componentsByType[ruleChainType]) { - componentsByType[ruleChainType] = {}; + if (!componentsByType[type]) { + componentsByType[type] = {}; } for (var i=componentTypes.length-1;i>=0;i--) { var componentType = componentTypes[i]; - if (componentsByType[ruleChainType][componentType]) { - result = result.concat(componentsByType[ruleChainType][componentType]); + if (componentsByType[type][componentType]) { + result = result.concat(componentsByType[type][componentType]); componentTypes.splice(i, 1); } } if (!componentTypes.length) { deferred.resolve(result); } else { - var url = '/api/components?componentTypes=' + componentTypes.join(',') + '&ruleChainType=' + ruleChainType; + var url = '/api/components?componentTypes=' + componentTypes.join(',') + '&ruleChainType=' + type; $http.get(url, null).then(function success(response) { var components = response.data; for (var i = 0; i < components.length; i++) { var component = components[i]; - var componentsList = componentsByType[ruleChainType][component.type]; + var componentsList = componentsByType[type][component.type]; if (!componentsList) { componentsList = []; - componentsByType[ruleChainType][component.type] = componentsList; + componentsByType[type][component.type] = componentsList; } componentsList.push(component); componentsByClazz[component.clazz] = component; @@ -65,4 +90,37 @@ function ComponentDescriptorService($http, $q) { } return deferred.promise; } + + function getComponentDescriptorByClazz(componentDescriptorClazz) { + var deferred = $q.defer(); + if (componentsByClazz[componentDescriptorClazz]) { + deferred.resolve(componentsByClazz[componentDescriptorClazz]); + } else { + var url = '/api/component/' + componentDescriptorClazz; + $http.get(url, null).then(function success(response) { + componentsByClazz[componentDescriptorClazz] = response.data; + deferred.resolve(componentsByClazz[componentDescriptorClazz]); + }, function fail() { + deferred.reject(); + }); + } + return deferred.promise; + } + + function getPluginActionsByPluginClazz(pluginClazz) { + var deferred = $q.defer(); + if (actionsByPlugin[pluginClazz]) { + deferred.resolve(actionsByPlugin[pluginClazz]); + } else { + var url = '/api/components/actions/' + pluginClazz; + $http.get(url, null).then(function success(response) { + actionsByPlugin[pluginClazz] = response.data; + deferred.resolve(actionsByPlugin[pluginClazz]); + }, function fail() { + deferred.reject(); + }); + } + return deferred.promise; + } + } diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index 56e4378eba..091c1287ba 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -42,7 +42,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device getRelatedEntity: getRelatedEntity, deleteRelatedEntity: deleteRelatedEntity, moveEntity: moveEntity, - copyEntity: copyEntity + copyEntity: copyEntity, + getAssignedToEdgeEntitiesByType: getAssignedToEdgeEntitiesByType }; return service; @@ -1684,4 +1685,26 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device } } + function getAssignedToEdgeEntitiesByType(edgeId, entityType, pageLink) { + var promise; + switch (entityType) { + case types.entityType.device: + promise = deviceService.getEdgeDevices(edgeId, pageLink, null); + break; + case types.entityType.asset: + promise = assetService.getEdgeAssets(edgeId, pageLink, null); + break; + case types.entityType.entityView: + promise = entityViewService.getEdgeEntityViews(edgeId, pageLink, null); + break; + case types.entityType.dashboard: + promise = dashboardService.getEdgeDashboards(edgeId, pageLink, null); + break; + case types.entityType.rulechain: + promise = ruleChainService.getEdgeRuleChains(edgeId, pageLink, null); + break; + } + return promise; + } + } diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js index 4b706e827d..742f102d28 100644 --- a/ui/src/app/api/widget.service.js +++ b/ui/src/app/api/widget.service.js @@ -22,6 +22,7 @@ import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-wid import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; import thingsboardEntitiesHierarchyWidget from '../widget/lib/entities-hierarchy-widget'; +import thingsboardEdgesOverviewWidget from '../widget/lib/edges-overview-widget' import thingsboardExtensionsTableWidget from '../widget/lib/extensions-table-widget'; import thingsboardDateRangeNavigatorWidget from '../widget/lib/date-range-navigator/date-range-navigator'; import thingsboardMultipleInputWidget from '../widget/lib/multiple-input-widget'; @@ -52,7 +53,7 @@ import thingsboardUtils from '../common/utils.service'; export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, - thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget, + thingsboardEntitiesHierarchyWidget, thingsboardEdgesOverviewWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget, thingsboardMultipleInputWidget, thingsboardWebCameraInputWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils, thingsboardJsonToString, TripAnimationWidget]) .factory('widgetService', WidgetService) diff --git a/ui/src/app/components/nav-tree.scss b/ui/src/app/components/nav-tree.scss index df903e747b..da50ccea5e 100644 --- a/ui/src/app/components/nav-tree.scss +++ b/ui/src/app/components/nav-tree.scss @@ -131,6 +131,12 @@ content: "supervisor_account"; } } + + &.tb-rule-chain-group { + &::before { + content: "settings_ethernet"; + } + } } } } diff --git a/ui/src/app/customer/customer-fieldset.tpl.html b/ui/src/app/customer/customer-fieldset.tpl.html index a67a4391df..f24eb3048b 100644 --- a/ui/src/app/customer/customer-fieldset.tpl.html +++ b/ui/src/app/customer/customer-fieldset.tpl.html @@ -19,7 +19,7 @@ {{ 'customer.manage-assets' | translate }} {{ 'customer.manage-devices' | translate }} {{ 'customer.manage-dashboards' | translate }} -{{ 'customer.manage-edges' | translate }} +{{ 'customer.manage-edges' | translate }} {{ 'customer.delete' | translate }}
diff --git a/ui/src/app/customer/customer.controller.js b/ui/src/app/customer/customer.controller.js index 2d1d2202a8..8c20cddb6b 100644 --- a/ui/src/app/customer/customer.controller.js +++ b/ui/src/app/customer/customer.controller.js @@ -21,7 +21,7 @@ import customerCard from './customer-card.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function CustomerController(customerService, $state, $stateParams, $translate, types) { +export default function CustomerController(customerService, $state, $stateParams, $translate, types, userService) { var customerActionsList = [ { @@ -76,21 +76,29 @@ export default function CustomerController(customerService, $state, $stateParams } }, icon: "dashboard" - }, - { - onAction: function ($event, item) { - openCustomerEdges($event, item); - }, - name: function() { return $translate.instant('edge.edge-instances') }, - details: function(customer) { - if (customer && customer.additionalInfo && customer.additionalInfo.isPublic) { - return $translate.instant('customer.manage-public-edges') - } else { - return $translate.instant('customer.manage-customer-edges') - } - }, - icon: "router" - }, + }]; + + if (userService.isEdgesSupportEnabled()) { + customerActionsList.push( + { + onAction: function ($event, item) { + openCustomerEdges($event, item); + }, + name: function() { return $translate.instant('edge.edge-instances') }, + details: function(customer) { + if (customer && customer.additionalInfo && customer.additionalInfo.isPublic) { + return $translate.instant('customer.manage-public-edges') + } else { + return $translate.instant('customer.manage-customer-edges') + } + }, + icon: "router", + isEnabled: false + } + ) + } + + customerActionsList.push( { onAction: function ($event, item) { vm.grid.deleteItem($event, item); @@ -102,12 +110,14 @@ export default function CustomerController(customerService, $state, $stateParams return customer && (!customer.additionalInfo || !customer.additionalInfo.isPublic); } } - ]; + ); var vm = this; vm.types = types; + vm.isEdgesSupportEnabled = userService.isEdgesSupportEnabled(); + vm.customerGridConfig = { refreshParamsFunc: null, diff --git a/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js b/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js index 16015d12db..bbaf1da30d 100644 --- a/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js +++ b/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js @@ -92,14 +92,6 @@ export default function EdgeDownlinksRowDirective($compile, $templateCache, $mdD type === types.edgeEventType.widgetsBundle ); } - scope.checkTooltip = function($event) { - var el = $event.target; - var $el = angular.element(el); - if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){ - $el.attr('title', $el.text()); - } - } - $compile(element.contents())(scope); scope.updateStatus = function(downlinkCreatedTime) { diff --git a/ui/src/app/edge/edge-fieldset.tpl.html b/ui/src/app/edge/edge-fieldset.tpl.html index 7058d44000..87329291e8 100644 --- a/ui/src/app/edge/edge-fieldset.tpl.html +++ b/ui/src/app/edge/edge-fieldset.tpl.html @@ -44,7 +44,7 @@ class="md-raised md-primary">{{ 'edge.dashboards' | translate }} {{ 'edge.rulechain-templates' | translate }} + class="md-raised md-primary">{{ 'edge.manage-edge-rulechains' | translate }}
@@ -57,20 +57,25 @@ edge.copy-edge-key edge.copy-edge-secret edge.sync @@ -102,16 +107,16 @@ ng-model="edge.type" entity-type="types.entityType.edge"> -
edge.license-key-hint
- +
edge.license-key-hint
+
edge.edge-license-key-required
-
edge.cloud-endpoint-hint
- +
edge.cloud-endpoint-hint
+
@@ -119,7 +124,7 @@
-
+
@@ -134,7 +139,7 @@
-
+
diff --git a/ui/src/app/edge/edge.directive.js b/ui/src/app/edge/edge.directive.js index 7f8e6c3436..99fd7e6dc3 100644 --- a/ui/src/app/edge/edge.directive.js +++ b/ui/src/app/edge/edge.directive.js @@ -20,7 +20,7 @@ import edgeFieldsetTemplate from './edge-fieldset.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService, edgeService) { +export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService, edgeService, userService) { var linker = function (scope, element) { var template = $templateCache.get(edgeFieldsetTemplate); element.html(template); @@ -29,6 +29,7 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD scope.isAssignedToCustomer = false; scope.isPublic = false; scope.assignedCustomer = null; + scope.isTenantAdmin = userService.getCurrentUser().authority === 'TENANT_ADMIN'; scope.$watch('edge', function(newVal) { if (newVal) { diff --git a/ui/src/app/edge/edge.routes.js b/ui/src/app/edge/edge.routes.js index 5a8a2bbcc7..f89dfdb1b7 100644 --- a/ui/src/app/edge/edge.routes.js +++ b/ui/src/app/edge/edge.routes.js @@ -21,8 +21,6 @@ import devicesTemplate from "../device/devices.tpl.html"; import assetsTemplate from "../asset/assets.tpl.html"; import dashboardsTemplate from "../dashboard/dashboards.tpl.html"; import dashboardTemplate from "../dashboard/dashboard.tpl.html"; -import ruleChainsTemplate from "../rulechain/rulechains.tpl.html"; -import ruleChainTemplate from "../rulechain/rulechain.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ @@ -180,62 +178,5 @@ export default function EdgeRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "router", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}' } - }).state('home.edges.ruleChains', { - url: '/:edgeId/ruleChains', - params: {'topIndex': 0}, - module: 'private', - auth: ['TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: ruleChainsTemplate, - controllerAs: 'vm', - controller: 'RuleChainsController' - } - }, - data: { - searchEnabled: true, - pageTitle: 'edge.rulechain-templates', - ruleChainsType: 'edge' - }, - ncyBreadcrumb: { - label: '{"icon": "settings_ethernet", "label": "edge.rulechain-templates"}' - } - }).state('home.edges.ruleChains.ruleChain', { - url: '/:ruleChainId', - reloadOnSearch: false, - module: 'private', - auth: ['SYS_ADMIN', 'TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: ruleChainTemplate, - controller: 'RuleChainController', - controllerAs: 'vm' - } - }, - resolve: { - ruleChain: - /*@ngInject*/ - function($stateParams, ruleChainService) { - return ruleChainService.getRuleChain($stateParams.ruleChainId); - }, - ruleChainMetaData: - /*@ngInject*/ - function($stateParams, ruleChainService) { - return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId); - }, - ruleNodeComponents: - /*@ngInject*/ - function($stateParams, ruleChainService) { - return ruleChainService.getRuleNodeComponents(types.ruleChainType.edge); - } - }, - data: { - import: false, - searchEnabled: false, - pageTitle: 'edge.rulechain-templates' - }, - ncyBreadcrumb: { - label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' - } }); } diff --git a/ui/src/app/edge/edges.tpl.html b/ui/src/app/edge/edges.tpl.html index ea40c80b06..d7724238f3 100644 --- a/ui/src/app/edge/edges.tpl.html +++ b/ui/src/app/edge/edges.tpl.html @@ -65,7 +65,7 @@ default-event-type="{{vm.types.eventType.error.value}}"> - + diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index 1f495ccdf0..8fdef0c489 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -811,7 +811,66 @@ "event-action": "Ereignisaktion", "load-entity-error": "Entität nicht gefunden. Fehler beim Laden der Informationen", "unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Kanten nicht zugewiesen und sind für den Kunden nicht zugänglich.", - "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, 1 {1 Rand} other {# Rand} }?" + "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, 1 {1 Rand} other {# Rand} }?", + "edge-file": "Edge-Datei", + "name-starts-with": "Der Kantenname beginnt mit", + "rulechain-templates": "Regelkettenvorlagen", + "rulechain-template": "Regelkettenvorlage", + "unassign-edges-action-title": "Heben Sie die Zuordnung von {count, plural, 1 {1 edge} other {# edge}} vom Kunden auf", + "enter-edge-type": "Geben Sie den Kantentyp ein", + "no-edge-types-matching": "Es wurden keine Kantentypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "edge-type-list-empty": "Keine Kantentypen ausgewählt.", + "edge-types": "Kantentypen", + "license-key-hint": "Um Ihre Lizenz zu erhalten, navigieren Sie zur Preisseite und wählen Sie die beste Lizenzoption für Ihre aus Fall.", + "cloud-endpoint-hint": "Edge erfordert HTTP-Zugriff auf die Cloud (ThingsBoard CE / PE), um den Lizenzschlüssel zu überprüfen. Bitte geben Sie die Cloud-URL an, zu der Edge eine Verbindung herstellen kann.", + "missing-related-rule-chains-title": "In Edge fehlen verwandte Regelketten.", + "missing-related-rule-chains-text": "Randregelkette (n) zugewiesen Verwenden Sie Regelknoten, die Nachrichten an Regelkette (n) weiterleiten, die dieser Kante nicht zugeordnet sind.

Liste der fehlenden Regelketten:
{{missingRuleChains}}", + "downlinks": "Downlinks", + "no-downlinks-prompt": "Keine Downlinks gefunden", + "assigned-to-customer-widget": "Zugewiesen an: {{customerTitle}}", + "widget-datasource-error": "Dieses Widget unterstützt nur EDGE-Entitätsdatenquellen" + }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Device", + "type-device-profile": "Device Profile", + "type-entity-view": "Entity View", + "type-alarm": "Alar", + "type-rule-chain": "Rule Chain", + "type-rule-chain-metadata": "Rule Chain Metadata", + "type-edge": "Edge", + "type-entity-group": "Entity Group", + "type-scheduler-event": "Scheduler Event", + "type-white-labeling": "White Labeling", + "type-login-white-labeling": "White Labeling Login", + "type-user": "User", + "type-tenant": "Tenant", + "type-customer": "Customer", + "type-custom-translation": "Custom Translation", + "type-relation": "Relation", + "type-widgets-bundle": "Widgets Bundle", + "type-widgets-type": "Widgets Type", + "type-admin-settings": "Admin Settings", + "action-type-added": "Added", + "action-type-deleted": "Deleted", + "action-type-updated": "Updated", + "action-type-post-attributes": "Post Attributes", + "action-type-attributes-updated": "Attributes Updated", + "action-type-attributes-deleted": "Attributes Deleted", + "action-type-timeseries-updated": "Timeseries Updated", + "action-type-credentials-updated": "Credentials Updated", + "action-type-assigned-to-customer": "Assigned to Customer", + "action-type-unassigned-from-customer": "Unassigned from Customer", + "action-type-relation-add-or-update": "Relation Add or Update", + "action-type-relation-deleted": "Relation Deleted", + "action-type-rpc-call": "RPC Call", + "action-type-alarm-ack": "Alarm Ack", + "action-type-alarm-clear": "Alarm Clear", + "action-type-assigned-to-edge": "Assigned to Edge", + "action-type-unassigned-from-edge": "Unassigned from Edge", + "action-type-credentials-request": "Credentials Request", + "action-type-entity-merge-request": "Entity Merge Request" }, "error": { "unable-to-connect": "Es konnte keine Verbindung zum Server hergestellt werden! Bitte überprüfen Sie Ihre Internetverbindung.", @@ -1409,7 +1468,11 @@ "unset-auto-assign-to-edge": "Weisen Sie Kanten bei der Erstellung keine Regelkette zu", "unset-auto-assign-to-edge-title": "Sind Sie sicher, dass Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung nicht den Kanten zuweisen möchten?", "unset-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung nicht mehr automatisch den Kanten zugewiesen.", - "edge-template-root": "Vorlagenstamm" + "edge-template-root": "Vorlagenstamm", + "set-auto-assign-to-edge-card": "Bei der Erstellung den Kanten zuweisen", + "set-default-root-edge": "Machen Sie die Regelkette zum Standardstamm", + "set-default-root-edge-rulechain-title": "Sind Sie sicher, dass Sie die Standardkettenwurzel der Regelkette '{{ruleChainName}}' festlegen möchten?", + "set-default-root-edge-rulechain-text": "Nach der Bestätigung wird die Regelkette zum Standard-Edge-Root und verarbeitet alle eingehenden Transportnachrichten." }, "rulenode": { "details": "Details", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 9dc54330bb..5aa770f21c 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -769,7 +769,6 @@ "management": "Edge management", "no-edges-matching": "No edges matching '{{entity}}' were found.", "add": "Add Edge", - "view": "View Edge", "no-edges-text": "No edges found", "edge-details": "Edge details", "add-edge-text": "Add new edge", @@ -794,7 +793,6 @@ "id-copied-message": "Edge Id has been copied to clipboard", "sync": "Sync Edge", "sync-message": "Edge has been synchronized", - "permissions": "Permissions", "edge-required": "Edge required", "edge-type": "Edge type", "edge-type-required": "Edge type is required.", @@ -856,7 +854,9 @@ "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge.

List of missing rule chain(s):
{{missingRuleChains}}", "downlinks": "Downlinks", - "no-downlinks-prompt": "No downlinks found" + "no-downlinks-prompt": "No downlinks found", + "assigned-to-customer-widget": "Assigned to: {{customerTitle}}", + "widget-datasource-error": "This widget supports only EDGE entity datasource" }, "edge-event": { "type-dashboard": "Dashboard", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 07acd04e3a..9083bf966b 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -747,7 +747,6 @@ "management": "Gestión de bordes", "no-edges-matching": "No se encontraron bordes que coincidan con '{{entity}}'", "add": "Agregar borde", - "view": "Ver borde", "no-edges-text": "No se encontraron bordes", "edge-details": "Detalles del borde", "add-edge-text": "Agregar nuevo borde", @@ -771,7 +770,6 @@ "id-copied-message": "El ID de borde se ha copiado al portapapeles", "sync": "Sinc Edge", "sync-message": "Edge se ha sincronizado", - "permissions": "Permisos", "edge-required": "Edge required", "edge-type": "Type de la bordure", "edge-type-required": "El tipo de borde es requerido.", @@ -824,8 +822,67 @@ "event-action": "Información de la entidad", "load-entity-error": "Entidad no encontrada. No se pudo cargar la información", "unassign-edges-text": "Después de la confirmación de todos los bordes seleccionados, se anulará la asignación y el cliente no podrá acceder a ellos.", - "unassign-edges-title": "¿Está seguro de que desea anular la asignación de {count, plural, 1 {1 borde} other {# bordes}}?" + "unassign-edges-title": "¿Está seguro de que desea anular la asignación de {count, plural, 1 {1 borde} other {# bordes}}?", + "edge-file": "Archivo de borde", + "name-starts-with": "Edge name starts with", + "rulechain-templates": "Plantillas de cadena de reglas", + "rulechain-template": "Plantilla de cadena de reglas", + "unassign-edges-action-title": "Anular la asignación de {count, plural, 1 {1 borde} other {# bordes}} del cliente", + "enter-edge-type": "Ingrese el tipo de borde", + "no-edge-types-matching": "No se encontraron tipos de aristas que coincidan con '{{entitySubtype}}'.", + "edge-type-list-empty": "No se seleccionó ningún tipo de borde.", + "edge-types": "Tipos de bordes", + "license-key-hint": "Para obtener su licencia, vaya a la página de precios y seleccione la mejor opción de licencia para su caso.", + "cloud-endpoint-hint": "Edge requiere acceso HTTP (s) a la nube (ThingsBoard CE / PE) para verificar la clave de licencia. Especifique la URL de la nube a la que Edge puede conectarse.", + "missing-related-rule-chains-title": "Al borde le faltan cadenas de reglas relacionadas", + "missing-related-rule-chains-text": "Asignado a la (s) cadena (s) de reglas de borde usa nodos de reglas que reenvían mensajes a cadenas de reglas que no están asignadas a este borde.

Lista de cadenas de reglas faltantes:
{{missingRuleChains}}", + "downlinks": "Enlaces descendentes", + "no-downlinks-prompt": "No se encontraron enlaces descendentes", + "assigned-to-customer-widget": "Asignado a: {{customerTitle}}", + "widget-datasource-error": "Este widget solo admite la fuente de datos de la entidad EDGE" }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Device", + "type-device-profile": "Device Profile", + "type-entity-view": "Entity View", + "type-alarm": "Alar", + "type-rule-chain": "Rule Chain", + "type-rule-chain-metadata": "Rule Chain Metadata", + "type-edge": "Edge", + "type-entity-group": "Entity Group", + "type-scheduler-event": "Scheduler Event", + "type-white-labeling": "White Labeling", + "type-login-white-labeling": "White Labeling Login", + "type-user": "User", + "type-tenant": "Tenant", + "type-customer": "Customer", + "type-custom-translation": "Custom Translation", + "type-relation": "Relation", + "type-widgets-bundle": "Widgets Bundle", + "type-widgets-type": "Widgets Type", + "type-admin-settings": "Admin Settings", + "action-type-added": "Added", + "action-type-deleted": "Deleted", + "action-type-updated": "Updated", + "action-type-post-attributes": "Post Attributes", + "action-type-attributes-updated": "Attributes Updated", + "action-type-attributes-deleted": "Attributes Deleted", + "action-type-timeseries-updated": "Timeseries Updated", + "action-type-credentials-updated": "Credentials Updated", + "action-type-assigned-to-customer": "Assigned to Customer", + "action-type-unassigned-from-customer": "Unassigned from Customer", + "action-type-relation-add-or-update": "Relation Add or Update", + "action-type-relation-deleted": "Relation Deleted", + "action-type-rpc-call": "RPC Call", + "action-type-alarm-ack": "Alarm Ack", + "action-type-alarm-clear": "Alarm Clear", + "action-type-assigned-to-edge": "Assigned to Edge", + "action-type-unassigned-from-edge": "Unassigned from Edge", + "action-type-credentials-request": "Credentials Request", + "action-type-entity-merge-request": "Entity Merge Request" + }, "error": { "unable-to-connect": "¡No se puede conectar al servidor! Por favor, revise su conexión a Internet.", "unhandled-error-code": "Código de error no controlado: {{errorCode}}", @@ -1477,7 +1534,11 @@ "unset-auto-assign-to-edge": "No asigne una cadena de reglas a los bordes en la creación", "unset-auto-assign-to-edge-title": "¿Está seguro de que no desea asignar la cadena de reglas de borde '{{ruleChainName}}' a los bordes en la creación?", "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde ya no se asignará automáticamente a los bordes en la creación.", - "edge-template-root": "Raíz de plantilla" + "edge-template-root": "Raíz de plantilla", + "set-auto-assign-to-edge-card": "Asignar a borde (s) en la creación", + "set-default-root-edge": "Hacer raíz predeterminada de la cadena de reglas", + "set-default-root-edge-rulechain-title": "¿Está seguro de que desea que la cadena de reglas '{{ruleChainName}}' sea la raíz del borde predeterminada?", + "set-default-root-edge-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en la raíz del borde predeterminada y manejará todos los mensajes de transporte entrantes." }, "rulenode": { "details": "Detalles", diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index a4b92cae37..97b0782534 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -830,8 +830,67 @@ "event-action": "Action d'événement", "load-entity-error": "Entité introuvable. Échec du chargement des informations", "unassign-edges-text": "Après la confirmation, tous les bordures sélectionnés ne seront plus attribués et ne seront pas accessibles par le client.", - "unassign-edges-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, 1 {1 bordure} other {# bordures}}?" + "unassign-edges-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, 1 {1 bordure} other {# bordures}}?", + "edge-file": "Fichier Edge", + "name-starts-with": "Le nom du bord commence par", + "rulechain-templates": "Modèles de chaîne de règles", + "rulechain-template": "Modèle de chaîne de règles", + "unassign-edges-action-title": "Annuler l'attribution de {count, plural, 1 {1 edge} other {# bords}} au client", + "enter-edge-type": "Entrez le type d'arête", + "no-edge-types-matching": "Aucun type d'arête correspondant à '{{entitySubtype}}' n'a été trouvé.", + "edge-type-list-empty": "Aucun type d'arête sélectionné.", + "edge-types": "Types de bords", + "license-key-hint": "Pour obtenir votre licence, accédez à la page de tarification et sélectionnez la meilleure option de licence pour votre Cas.", + "cloud-endpoint-hint": "Edge nécessite un accès HTTP (s) au Cloud (ThingsBoard CE / PE) pour vérifier la clé de licence. Veuillez spécifier l'URL cloud à laquelle Edge peut se connecter.", + "missing-related-rule-chains-title": "Edge n'a pas de chaîne (s) de règles associées", + "missing-related-rule-chains-text": "Les chaînes de règles affectées aux tronçons utilisent des nœuds de règles qui transfèrent les messages aux chaînes de règles non affectées à ce tronçon.

Liste des chaînes de règles manquantes:
{{missingRuleChains}}", + "downlinks": "Liens descendants", + "no-downlinks-prompt": "Aucun lien descendant trouvé", + "assigned-to-customer-widget": "Attribué à: {{customerTitle}}", + "widget-datasource-error": "Ce widget prend en charge uniquement la source de données d'entité EDGE" }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Device", + "type-device-profile": "Device Profile", + "type-entity-view": "Entity View", + "type-alarm": "Alar", + "type-rule-chain": "Rule Chain", + "type-rule-chain-metadata": "Rule Chain Metadata", + "type-edge": "Edge", + "type-entity-group": "Entity Group", + "type-scheduler-event": "Scheduler Event", + "type-white-labeling": "White Labeling", + "type-login-white-labeling": "White Labeling Login", + "type-user": "User", + "type-tenant": "Tenant", + "type-customer": "Customer", + "type-custom-translation": "Custom Translation", + "type-relation": "Relation", + "type-widgets-bundle": "Widgets Bundle", + "type-widgets-type": "Widgets Type", + "type-admin-settings": "Admin Settings", + "action-type-added": "Added", + "action-type-deleted": "Deleted", + "action-type-updated": "Updated", + "action-type-post-attributes": "Post Attributes", + "action-type-attributes-updated": "Attributes Updated", + "action-type-attributes-deleted": "Attributes Deleted", + "action-type-timeseries-updated": "Timeseries Updated", + "action-type-credentials-updated": "Credentials Updated", + "action-type-assigned-to-customer": "Assigned to Customer", + "action-type-unassigned-from-customer": "Unassigned from Customer", + "action-type-relation-add-or-update": "Relation Add or Update", + "action-type-relation-deleted": "Relation Deleted", + "action-type-rpc-call": "RPC Call", + "action-type-alarm-ack": "Alarm Ack", + "action-type-alarm-clear": "Alarm Clear", + "action-type-assigned-to-edge": "Assigned to Edge", + "action-type-unassigned-from-edge": "Unassigned from Edge", + "action-type-credentials-request": "Credentials Request", + "action-type-entity-merge-request": "Entity Merge Request" + }, "entity": { "add-alias": "Ajouter un alias d'entité", "alarm-name-starts-with": "Les actifs dont le nom commence par '{{prefix}}'", @@ -1455,7 +1514,11 @@ "unset-auto-assign-to-edge": "N'attribuez pas de chaîne de règles aux arêtes lors de la création", "unset-auto-assign-to-edge-title": "Êtes-vous sûr de ne pas vouloir attribuer la chaîne de règles d'arête '{{ruleChainName}}' à l'arête (s) lors de la création?", "unset-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arêtes ne sera plus automatiquement affectée aux arêtes lors de la création.", - "edge-template-root": "Racine du modèle" + "edge-template-root": "Racine du modèle", + "set-auto-assign-to-edge-card": "Affecter aux arêtes lors de la création", + "set-default-root-edge": "Rendre la chaîne de règles racine par défaut", + "set-default-root-edge-rulechain-title": "Voulez-vous vraiment définir la racine de périphérie par défaut de la chaîne de règles '{{ruleChainName}}'?", + "set-default-root-edge-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine périphérique par défaut et gérera tous les messages de transport entrants." }, "rulenode": { "add": "Ajouter un noeud de règle", diff --git a/ui/src/app/rulechain/rulechain-fieldset.tpl.html b/ui/src/app/rulechain/rulechain-fieldset.tpl.html index 6a4841a550..1f95855d96 100644 --- a/ui/src/app/rulechain/rulechain-fieldset.tpl.html +++ b/ui/src/app/rulechain/rulechain-fieldset.tpl.html @@ -24,8 +24,11 @@ class="md-raised md-primary">{{ 'rulechain.delete' | translate }}
{{ 'rulechain.set-root' | translate }} + {{ 'edge.unassign-from-edge' | translate }} {{ 'rulechain.set-edge-template-root-rulechain' | translate }} @@ -36,9 +39,7 @@ ng-show="!isEdit && !ruleChain.root && ruleChain.isDefault && ruleChainsScope == 'edges'" class="md-raised md-primary">{{ 'rulechain.unset-auto-assign-to-edge' | translate }} - {{ 'rulechain.set-root' | translate }} +
mapRuleChainsWithDefaultEdges(ruleChains)) + .then(ruleChains => deferred.resolve(ruleChains)); return deferred.promise; } } @@ -399,9 +391,9 @@ export default function RuleChainsController(ruleChainService, userService, impo } var ruleChainParams = {ruleChainId: ruleChain.id.id}; if (vm.ruleChainsScope === 'edge') { - $state.go('home.edges.ruleChains.ruleChain', Object.assign(ruleChainParams, edgeId = vm.edge.id.id)); + $state.go('home.edges.ruleChains.ruleChain', ruleChainParams); } else if (vm.ruleChainsScope === 'edges') { - $state.go('home.edges.edgeRuleChains.ruleChain', ruleChainParams); + $state.go('home.edges.ruleChains.ruleChain', ruleChainParams); } else { $state.go('home.ruleChains.ruleChain', ruleChainParams); } @@ -416,7 +408,7 @@ export default function RuleChainsController(ruleChainService, userService, impo } function isRootRuleChain(ruleChain) { - if (angular.isDefined(vm.edge) && vm.edge != null) { + if (vm.edge != null) { return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id === ruleChain.id.id; } else { return ruleChain && ruleChain.root; @@ -424,7 +416,7 @@ export default function RuleChainsController(ruleChainService, userService, impo } function isNonRootRuleChain(ruleChain) { - if (angular.isDefined(vm.edge) && vm.edge != null) { + if (vm.edge != null) { return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id !== ruleChain.id.id; } else { return ruleChain && !ruleChain.root; @@ -454,7 +446,7 @@ export default function RuleChainsController(ruleChainService, userService, impo .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { - if (angular.isDefined(vm.edge) && vm.edge != null) { + if (vm.edge != null) { edgeService.setRootRuleChain(vm.edge.id.id, ruleChain.id.id).then( (edge) => { vm.edge = edge; @@ -525,7 +517,7 @@ export default function RuleChainsController(ruleChainService, userService, impo }); } - function unassignRuleChainsFromEdge($event, items, edgeId) { + function unassignRuleChainsFromEdge($event, items) { var confirm = $mdDialog.confirm() .targetEvent($event) .title($translate.instant('rulechain.unassign-rulechains-title', {count: items.selectedCount}, 'messageformat')) @@ -567,7 +559,7 @@ export default function RuleChainsController(ruleChainService, userService, impo controller: 'AddRuleChainsToEdgeController', controllerAs: 'vm', templateUrl: addRuleChainsToEdgeTemplate, - locals: {edgeId: edgeId, ruleChains: ruleChains}, + locals: {edgeId, ruleChains}, parent: angular.element($document[0].body), fullscreen: true, targetEvent: $event @@ -604,7 +596,7 @@ export default function RuleChainsController(ruleChainService, userService, impo }); } - function unassignFromEdge($event, ruleChain, edgeId) { + function unassignFromEdge($event, ruleChain) { if ($event) { $event.stopPropagation(); } diff --git a/ui/src/app/rulechain/rulechains.tpl.html b/ui/src/app/rulechain/rulechains.tpl.html index 94ca52f3c6..26aa54fd72 100644 --- a/ui/src/app/rulechain/rulechains.tpl.html +++ b/ui/src/app/rulechain/rulechains.tpl.html @@ -30,6 +30,7 @@ edge="vm.edge" on-set-auto-assign-to-edge-rule-chain="vm.setAutoAssignToEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)" on-unset-auto-assign-to-edge-rule-chain="vm.unsetAutoAssignToEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)" + on-unassign-from-edge="vm.unassignFromEdge(event, vm.grid.detailsConfig.currentItem)" on-set-root-rule-chain="vm.setRootRuleChain(event, vm.grid.detailsConfig.currentItem)" on-export-rule-chain="vm.exportRuleChain(event, vm.grid.detailsConfig.currentItem)" on-delete-rule-chain="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> diff --git a/ui/src/app/services/menu.service.js b/ui/src/app/services/menu.service.js index 2d01daccdf..e6055654c9 100644 --- a/ui/src/app/services/menu.service.js +++ b/ui/src/app/services/menu.service.js @@ -202,7 +202,7 @@ function Menu(userService, $state, $rootScope) { { name: 'edge.rulechain-templates', type: 'link', - state: 'home.edges.edgeRuleChains', + state: 'home.edges.ruleChains', icon: 'settings_ethernet' } ] @@ -293,7 +293,7 @@ function Menu(userService, $state, $rootScope) { { name: 'edge.rulechain-templates', icon: 'settings_ethernet', - state: 'home.edges.edgeRuleChains' + state: 'home.edges.ruleChains' } ] diff --git a/ui/src/app/widget/lib/edges-overview-widget.js b/ui/src/app/widget/lib/edges-overview-widget.js new file mode 100644 index 0000000000..26c153a5a4 --- /dev/null +++ b/ui/src/app/widget/lib/edges-overview-widget.js @@ -0,0 +1,240 @@ +/* + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import './edges-overview-widget.scss'; + +/* eslint-disable import/no-unresolved, import/default */ +import edgesOverviewWidgetTemplate from './edges-overview-widget.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +export default angular.module('thingsboard.widgets.edgesOverviewWidget', []) + .directive('tbEdgesOverviewWidget', EdgesOverviewWidget) + .name; +/* eslint-disable no-unused-vars, no-undef */ +/*@ngInject*/ +function EdgesOverviewWidget() { + return { + restrict: "E", + scope: true, + bindToController: { + ctx: '=' + }, + controller: EdgesOverviewWidgetController, + controllerAs: 'vm', + templateUrl: edgesOverviewWidgetTemplate + }; +} + +/*@ngInject*/ +function EdgesOverviewWidgetController($scope, $translate, types, utils, entityService, edgeService, customerService, userService) { + var vm = this; + + vm.showData = true; + + vm.nodeIdCounter = 0; + + vm.entityNodesMap = {}; + vm.entityGroupsNodesMap = {}; + + vm.customerTitle = null; + vm.edgeIsDatasource = true; + + var edgeGroupsTypes = [ + types.entityType.asset, + types.entityType.device, + types.entityType.entityView, + types.entityType.dashboard, + types.entityType.rulechain + ]; + + vm.onNodeSelected = onNodeSelected; + + $scope.$watch('vm.ctx', function() { + if (vm.ctx && vm.ctx.defaultSubscription) { + vm.settings = vm.ctx.settings; + vm.widgetConfig = vm.ctx.widgetConfig; + vm.subscription = vm.ctx.defaultSubscription; + vm.datasources = vm.subscription.datasources; + updateDatasources(); + } + }); + + function onNodeSelected(node, event) { + var nodeId; + if (!node) { + nodeId = -1; + } else { + nodeId = node.id; + } + if (nodeId !== -1) { + var selectedNode = vm.entityNodesMap[nodeId]; + if (selectedNode) { + var descriptors = vm.ctx.actionsApi.getActionDescriptors('nodeSelected'); + if (descriptors.length) { + var entity = selectedNode.data.nodeCtx.entity; + vm.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx }); + } + } + } + } + + function updateDatasources() { + vm.loadNodes = loadNodes; + } + + function loadNodes(node, cb) { + var datasource = vm.datasources[0]; + if (node.id === '#' && datasource) { + if (datasource.type === types.datasourceType.entity && datasource.entity && datasource.entity.id.entityType === types.entityType.edge) { + var selectedEdge = datasource.entity; + vm.customerTitle = getCustomerTitle(selectedEdge.id.id); + // vm.ctx.widgetTitle = selectedEdge.name; + cb(loadNodesForEdge(selectedEdge.id.id, selectedEdge)); + } else if (datasource.type === types.datasourceType.function) { + cb(loadNodesForEdge(null, null)); + } else { + vm.edgeIsDatasource = false; + cb([]); + } + } else if (node.data && node.data.entity && node.data.entity.id.entityType === types.entityType.edge) { + var edgeId = node.data.entity.id.id; + var entityType = node.data.entityType; + var pageLink = {limit: 100}; + entityService.getAssignedToEdgeEntitiesByType(edgeId, entityType, pageLink).then( + (entities) => { + if (entities.data.length > 0) { + cb(entitiesToNodes(node.id, entities.data)); + } else { + cb([]); + } + } + ); + } else { + cb([]); + } + } + + function entitiesToNodes(parentNodeId, entities) { + var nodes = []; + vm.entityNodesMap[parentNodeId] = {}; + if (entities) { + entities.forEach( + (entity) => { + var node = createEntityNode(parentNodeId, entity, entity.id.entityType); + nodes.push(node); + } + ); + } + return nodes; + } + + function createEntityNode(parentNodeId, entity, entityType) { + var nodesMap = vm.entityNodesMap[parentNodeId]; + if (!nodesMap) { + nodesMap = {}; + vm.entityNodesMap[parentNodeId] = nodesMap; + } + var node = { + id: ++vm.nodeIdCounter, + icon: 'material-icons ' + iconForGroupType(entityType), + text: entity.name, + children: false, + data: { + entity, + internalId: entity.id.id + } + }; + nodesMap[entity.id.id] = node.id; + return node; + } + + function loadNodesForEdge(parentNodeId, entity) { + var nodes = []; + vm.entityGroupsNodesMap[parentNodeId] = {}; + var allowedGroupTypes = edgeGroupsTypes; + if (userService.getAuthority() === 'CUSTOMER_USER') { + allowedGroupTypes = edgeGroupsTypes.filter(type => type !== types.entityType.rulechain); + } + allowedGroupTypes.forEach( + (entityType) => { + var node = { + id: ++vm.nodeIdCounter, + icon: 'material-icons ' + iconForGroupType(entityType), + text: textForGroupType(entityType), + children: true, + data: { + entityType, + entity, + internalId: entity ? entity.id.id + '_' + entityType : utils.guid() + } + }; + nodes.push(node); + } + ) + return nodes; + } + + function iconForGroupType(entityType) { + switch (entityType) { + case types.entityType.asset: + return 'tb-asset-group'; + case types.entityType.device: + return 'tb-device-group'; + case types.entityType.entityView: + return 'tb-entity-view-group'; + case types.entityType.dashboard: + return 'tb-dashboard-group'; + case types.entityType.rulechain: + return 'tb-rule-chain-group'; + } + return ''; + } + + function textForGroupType(entityType) { + switch (entityType) { + case types.entityType.asset: + return $translate.instant('asset.assets'); + case types.entityType.device: + return $translate.instant('device.devices'); + case types.entityType.entityView: + return $translate.instant('entity-view.entity-views'); + case types.entityType.rulechain: + return $translate.instant('rulechain.rulechains'); + case types.entityType.dashboard: + return $translate.instant('dashboard.dashboards'); + } + return ''; + } + + function getCustomerTitle(edgeId) { + edgeService.getEdge(edgeId, true).then( + function success(edge) { + if (edge.customerId.id !== types.id.nullUid) { + customerService.getCustomer(edge.customerId.id, { ignoreErrors: true }).then( + function success(customer) { + vm.customerTitle = $translate.instant('edge.assigned-to-customer-widget', { customerTitle: customer.title }); + }, + function fail() { + } + ); + } + }, + function fail() { + } + ) + } + +} diff --git a/ui/src/app/widget/lib/edges-overview-widget.scss b/ui/src/app/widget/lib/edges-overview-widget.scss new file mode 100644 index 0000000000..6b7ce3f26e --- /dev/null +++ b/ui/src/app/widget/lib/edges-overview-widget.scss @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-edges-overview { + .mat-subheader { + padding: 15px; + font-size: 14px; + font-weight: 400; + opacity: .5; + } + + .tb-entities-nav-tree-panel { + overflow-x: auto; + overflow-y: auto; + } +} + +@media (max-width: 768px) { + .tb-edges-overview { + .tb-entities-nav-tree-panel { + .tb-nav-tree-container { + &.jstree-proton-responsive { + .jstree-anchor { + div.node-icon { + width: 40px; + height: 40px; + margin: 0; + background-size: 24px 24px; + } + + md-icon.node-icon { + width: 40px; + min-width: 40px; + height: 40px; + min-height: 40px; + margin: 0; + + &.material-icons { /* stylelint-disable-line selector-max-class */ + font-size: 24px; + line-height: 40px; + } + } + } + } + } + } + } +} diff --git a/ui/src/app/widget/lib/edges-overview-widget.tpl.html b/ui/src/app/widget/lib/edges-overview-widget.tpl.html new file mode 100644 index 0000000000..884e5360c5 --- /dev/null +++ b/ui/src/app/widget/lib/edges-overview-widget.tpl.html @@ -0,0 +1,29 @@ + +
+
+ {{ vm.customerTitle }} + edge.widget-datasource-error +
+ +
+
+