Browse Source
Develop/2.6 edge Edges overview widget, languages update, hide sensitive info for customerspull/3957/head
committed by
GitHub
27 changed files with 728 additions and 154 deletions
@ -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": "<tb-edges-overview-widget \n ctx=\"ctx\">\n</tb-edges-overview-widget>", |
|||
"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\":{}}" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
@ -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() { |
|||
} |
|||
) |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<div class="tb-absolute-fill tb-edges-overview" layout="column"> |
|||
<div ng-show="vm.showData" flex class="tb-absolute-fill" layout="column"> |
|||
<span class="mat-subheader" ng-if="vm.customerTitle">{{ vm.customerTitle }}</span> |
|||
<span class="mat-subheader" ng-if="!vm.edgeIsDatasource" translate>edge.widget-datasource-error</span> |
|||
<div flex class="tb-entities-nav-tree-panel"> |
|||
<tb-nav-tree |
|||
on-node-selected="vm.onNodeSelected(node, event)" |
|||
load-nodes="vm.loadNodes" |
|||
></tb-nav-tree> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
Loading…
Reference in new issue