Commit e4e5cb35dc59bd192246f40032e821dfe06179da

Authored by Artem Babak
1 parent 0ab5f30b

Edges Quick Overview Widget

... ... @@ -15,11 +15,11 @@
15 15 "resources": [],
16 16 "templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>",
17 17 "templateCss": "",
18   - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.edgesOverviewWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\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",
19   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgesOverviewSettings\",\n \"properties\": {\n \"nodeRelationQueryFunction\": {\n \"title\": \"Node relations query function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeHasChildrenFunction\": {\n \"title\": \"Node has children function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeOpenedFunction\": {\n \"title\": \"Default node opened function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeDisabledFunction\": {\n \"title\": \"Node disabled function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeIconFunction\": {\n \"title\": \"Node icon function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeTextFunction\": {\n \"title\": \"Node text function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodesSortFunction\": {\n \"title\": \"Nodes sort function: f(nodeCtx1, nodeCtx2)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"nodeRelationQueryFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeHasChildrenFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeOpenedFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeDisabledFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeIconFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeTextFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodesSortFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
20   - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {},\n \"required\": []\n },\n \"form\": []\n}",
21   - "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\":{\"nodeRelationQueryFunction\":\"/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" <b>\\\"+ data['temperature'] +\\\" °C</b>\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \"},\"title\":\"Edge Quick Overview Widget\",\"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\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"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\":{}}"
  18 + "controllerScript": "self.onInit = function() {\n};\n\nself.onDestroy = function() {\n};\n",
  19 + "settingsSchema": "{}",
  20 + "dataKeySettingsSchema": "{}\n",
  21 + "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\",\"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\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"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\":{}}"
22 22 }
23 23 }
24 24 ]
25   -}
\ No newline at end of file
  25 +}
... ...
... ... @@ -17,37 +17,13 @@
17 17 -->
18 18 <div class="tb-edges-overview tb-absolute-fill" tb-toast toastTarget="{{ toastTargetId }}">
19 19 <div fxFlex fxLayout="column" class="tb-absolute-fill">
20   - <mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
21   - <div class="mat-toolbar-tools">
22   - <button mat-button mat-icon-button
23   - matTooltip="{{ 'action.search' | translate }}"
24   - matTooltipPosition="above">
25   - <mat-icon>search</mat-icon>
26   - </button>
27   - <mat-form-field fxFlex>
28   - <mat-label>&nbsp;</mat-label>
29   - <input #searchInput matInput
30   - [(ngModel)]="textSearch"
31   - placeholder="{{ 'entity.search' | translate }}"/>
32   - </mat-form-field>
33   - <button mat-button mat-icon-button (click)="exitFilterMode()"
34   - matTooltip="{{ 'action.close' | translate }}"
35   - matTooltipPosition="above">
36   - <mat-icon>close</mat-icon>
37   - </button>
38   - </div>
39   - </mat-toolbar>
40 20 <div *ngIf="customerTitle" fxLayout="row" class="customer-info">
41 21 <span>{{ customerTitle }}</span>
42 22 </div>
43 23 <div fxFlex class="tb-entities-nav-tree-panel">
44 24 <tb-nav-tree
45 25 [loadNodes]="loadNodes"
46   - [onNodeSelected]="onNodeSelected"
47   - [onNodesInserted]="onNodesInserted"
48   - [editCallbacks]="nodeEditCallbacks"
49 26 enableSearch="true"
50   - [searchCallback]="searchCallback"
51 27 ></tb-nav-tree>
52 28 </div>
53 29 </div>
... ...
... ... @@ -14,63 +14,46 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { ChangeDetectorRef, AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
  17 +import {
  18 + AfterViewInit,
  19 + ChangeDetectorRef,
  20 + Component,
  21 + ElementRef,
  22 + Input,
  23 + OnInit,
  24 + ViewChild,
  25 + ViewContainerRef
  26 +} from '@angular/core';
18 27 import { PageComponent } from '@shared/components/page.component';
19 28 import { Store } from '@ngrx/store';
20 29 import { AppState } from '@core/core.state';
21 30 import { WidgetAction, WidgetContext } from '@home/models/widget-component.models';
22   -import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models';
23   -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
  31 +import { WidgetConfig } from '@shared/models/widget.models';
  32 +import { IWidgetSubscription } from '@core/api/widget-api.models';
24 33 import { UtilsService } from '@core/services/utils.service';
25 34 import cssjs from '@core/css/css';
26   -import { fromEvent, Observable } from 'rxjs';
27   -import { debounceTime, distinctUntilChanged, map, mergeMap, tap } from 'rxjs/operators';
  35 +import { fromEvent } from 'rxjs';
  36 +import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
28 37 import { constructTableCssString } from '@home/components/widget/lib/table-widget.models';
29 38 import { Overlay } from '@angular/cdk/overlay';
30 39 import {
31 40 LoadNodesCallback,
32   - NavTreeEditCallbacks,
33   - NodesCallback,
34   - NodeSearchCallback,
35   - NodeSelectedCallback,
36   - NodesInsertedCallback
  41 + NavTreeEditCallbacks
37 42 } from '@shared/components/nav-tree.component';
38 43 import { EntityType } from '@shared/models/entity-type.models';
39   -import { deepClone, hashCode } from '@core/utils';
  44 +import { hashCode } from '@core/utils';
40 45 import {
41   - defaultNodeIconFunction,
42   - defaultNodeOpenedFunction,
43   - defaultNodeRelationQueryFunction,
44   - defaultNodesSortFunction,
45   - EdgeGroupsNodeData,
  46 + EdgeGroupNodeData,
46 47 edgeGroupsNodeText,
47   - edgeGroupsTypes,
48   - EdgeNodeData,
  48 + edgeGroupsTypes, EntityNodeData,
49 49 edgeNodeText,
50   - EdgeOverviewNode, EdgesOverviewWidgetSettings,
51   - HierarchyNavTreeNode,
52   - HierarchyNodeContext,
53   - HierarchyNodeDatasource,
54   - iconUrlHtml,
55   - loadNodeCtxFunction,
56   - materialIconHtml,
57   - NodeDisabledFunction,
58   - NodeHasChildrenFunction,
59   - NodeIconFunction,
60   - NodeOpenedFunction,
61   - NodeRelationQueryFunction,
62   - NodesSortFunction,
63   - NodeTextFunction
  50 + EdgeOverviewNode, EntityNodeDatasource
64 51 } from '@home/components/widget/lib/edges-overview-widget.models';
65   -import { EntityRelationsQuery } from '@shared/models/relation.models';
66   -import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models';
67   -import { EntityFilter } from '@shared/models/query/query.models';
68 52 import { EdgeService } from "@core/http/edge.service";
69 53 import { EntityService } from "@core/http/entity.service";
70 54 import { TranslateService } from "@ngx-translate/core";
71   -import { Direction, SortOrder } from "@shared/models/page/sort-order";
72 55 import { PageLink } from "@shared/models/page/page-link";
73   -import { Edge, EdgeInfo } from "@shared/models/edge.models";
  56 +import { Edge } from "@shared/models/edge.models";
74 57 import { BaseData } from "@shared/models/base-data";
75 58 import { EntityId } from "@shared/models/id/entity-id";
76 59
... ... @@ -79,7 +62,7 @@ import { EntityId } from "@shared/models/id/entity-id";
79 62 templateUrl: './edges-overview-widget.component.html',
80 63 styleUrls: ['./edges-overview-widget.component.scss']
81 64 })
82   -export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit, AfterViewInit {
  65 +export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit {
83 66
84 67 @Input()
85 68 ctx: WidgetContext;
... ... @@ -87,44 +70,17 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
87 70 @ViewChild('searchInput') searchInputField: ElementRef;
88 71
89 72 public toastTargetId = 'edges-overview-' + this.utils.guid();
90   -
91   - public textSearchMode = false;
92   - public textSearch = null;
93 73 public customerTitle: string = null;
94 74
95   - public nodeEditCallbacks: NavTreeEditCallbacks = {};
96   -
97   - private settings: EdgesOverviewWidgetSettings;
98 75 private widgetConfig: WidgetConfig;
99 76 private subscription: IWidgetSubscription;
100   - private datasources: Array<HierarchyNodeDatasource>;
101   - private data: Array<Array<DatasourceData>>;
  77 + private datasources: Array<EntityNodeDatasource>;
102 78
103   - private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {};
104   - private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {};
105 79 private nodeIdCounter = 0;
106 80
107   - private nodeRelationQueryFunction: NodeRelationQueryFunction;
108   - private nodeIconFunction: NodeIconFunction;
109   - private nodeTextFunction: NodeTextFunction;
110   - private nodeDisabledFunction: NodeDisabledFunction;
111   - private nodeOpenedFunction: NodeOpenedFunction;
112   - private nodeHasChildrenFunction: NodeHasChildrenFunction;
113   - private nodesSortFunction: NodesSortFunction;
114   -
115 81 private edgeNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {};
116 82 private edgeGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {};
117 83
118   -
119   - private searchAction: WidgetAction = {
120   - name: 'action.search',
121   - show: false,
122   - icon: 'search',
123   - onAction: () => {
124   - this.enterFilterMode();
125   - }
126   - };
127   -
128 84 constructor(protected store: Store<AppState>,
129 85 private elementRef: ElementRef,
130 86 private edgeService: EdgeService,
... ... @@ -138,72 +94,19 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
138 94 }
139 95
140 96 ngOnInit(): void {
141   - this.ctx.$scope.edgesOverviewWidget = this;
142   - this.settings = this.ctx.settings;
143 97 this.widgetConfig = this.ctx.widgetConfig;
144 98 this.subscription = this.ctx.defaultSubscription;
145   - this.datasources = this.subscription.datasources as Array<HierarchyNodeDatasource>;
146   - this.data = this.subscription.dataPages[0].data;
147   - this.ctx.widgetTitle = this.datasources[0].entity.name;
148   - this.getCustomerTitle(this.datasources[0].entity.id.id);
  99 + this.datasources = this.subscription.datasources as Array<EntityNodeDatasource>;
  100 + if (this.datasources.length > 0 && this.datasources[0].entity.id.entityType === EntityType.EDGE) {
  101 + let selectedEdge = this.datasources[0].entity;
  102 + this.getCustomerTitle(selectedEdge.id.id);
  103 + this.ctx.widgetTitle = selectedEdge.name;
  104 + }
149 105 this.initializeConfig();
150 106 this.ctx.updateWidgetParams();
151 107 }
152 108
153   - ngAfterViewInit(): void {
154   - fromEvent(this.searchInputField.nativeElement, 'keyup')
155   - .pipe(
156   - debounceTime(150),
157   - distinctUntilChanged(),
158   - tap(() => {
159   - this.updateSearchNodes();
160   - })
161   - )
162   - .subscribe();
163   - }
164   -
165   - public onDataUpdated() {
166   - this.updateNodeData(this.subscription.data);
167   - }
168   -
169 109 private initializeConfig() {
170   - this.ctx.widgetActions = [this.searchAction];
171   -
172   - const testNodeCtx: HierarchyNodeContext = {
173   - entity: {
174   - id: {
175   - entityType: EntityType.DEVICE,
176   - id: '123'
177   - },
178   - name: 'TEST DEV1'
179   - },
180   - data: {},
181   - level: 2
182   - };
183   - const parentNodeCtx = deepClone(testNodeCtx);
184   - parentNodeCtx.level = 1;
185   - testNodeCtx.parentNodeCtx = parentNodeCtx;
186   -
187   - this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx);
188   - this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'nodeCtx', testNodeCtx);
189   - this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'nodeCtx', testNodeCtx);
190   - this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx);
191   - this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx);
192   - this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx);
193   -
194   - const testNodeCtx2 = deepClone(testNodeCtx);
195   - testNodeCtx2.entity.name = 'TEST DEV2';
196   -
197   - this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2);
198   -
199   - this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction;
200   - this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction;
201   - this.nodeTextFunction = this.nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name);
202   - this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false);
203   - this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction;
204   - this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true);
205   - this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction;
206   -
207 110 const cssString = constructTableCssString(this.widgetConfig);
208 111 const cssParser = new cssjs();
209 112 cssParser.testMode = false;
... ... @@ -213,72 +116,14 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
213 116 $(this.elementRef.nativeElement).addClass(namespace);
214 117 }
215 118
216   - private enterFilterMode() {
217   - this.textSearchMode = true;
218   - this.textSearch = '';
219   - this.ctx.hideTitlePanel = true;
220   - this.ctx.detectChanges(true);
221   - setTimeout(() => {
222   - this.searchInputField.nativeElement.focus();
223   - this.searchInputField.nativeElement.setSelectionRange(0, 0);
224   - }, 10);
225   - }
226   -
227   - exitFilterMode() {
228   - this.textSearchMode = false;
229   - this.textSearch = null;
230   - this.updateSearchNodes();
231   - this.ctx.hideTitlePanel = false;
232   - this.ctx.detectChanges(true);
233   - }
234   -
235   - private updateSearchNodes() {
236   - if (this.textSearch != null) {
237   - this.nodeEditCallbacks.search(this.textSearch);
238   - } else {
239   - this.nodeEditCallbacks.clearSearch();
240   - }
241   - }
242   -
243   - private updateNodeData(subscriptionData: Array<DatasourceData>) {
244   - const affectedNodes: string[] = [];
245   - if (subscriptionData) {
246   - subscriptionData.forEach((datasourceData) => {
247   - const datasource = datasourceData.datasource as HierarchyNodeDatasource;
248   - if (datasource.nodeId) {
249   - const node = this.nodesMap[datasource.nodeId];
250   - const key = datasourceData.dataKey.label;
251   - let value;
252   - if (datasourceData.data && datasourceData.data.length) {
253   - value = datasourceData.data[0][1];
254   - }
255   - if (node.data.nodeCtx.data[key] !== value) {
256   - if (affectedNodes.indexOf(datasource.nodeId) === -1) {
257   - affectedNodes.push(datasource.nodeId);
258   - }
259   - node.data.nodeCtx.data[key] = value;
260   - }
261   - }
262   - });
263   - }
264   - affectedNodes.forEach((nodeId) => {
265   - const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId);
266   - if (node) {
267   - this.updateNodeStyle(this.nodesMap[nodeId]);
268   - } else {
269   - this.pendingUpdateNodeTasks[nodeId] = () => {
270   - this.updateNodeStyle(this.nodesMap[nodeId]);
271   - };
272   - }
273   - });
274   - }
275   -
276 119 public loadNodes: LoadNodesCallback = (node, cb) => {
277   - if (node.id === '#') {
278   - const edge: BaseData<EntityId> = this.datasources[0].entity;
279   - cb(this.loadNodesForEdge(edge.id.id, edge));
280   - }
281   - else if (node.data.type === 'edgeGroups') {
  120 + var selectedEdge: BaseData<EntityId> = null;
  121 + if (this.datasources.length > 0 && this.datasources[0].entity && this.datasources[0].entity.id.entityType === EntityType.EDGE) {
  122 + selectedEdge = this.datasources[0].entity;
  123 + }
  124 + if (node.id === '#' && selectedEdge) {
  125 + cb(this.loadNodesForEdge(selectedEdge.id.id, selectedEdge));
  126 + } else if (node.data && node.data.type === 'edgeGroup') {
282 127 const pageLink = new PageLink(100);
283 128 this.entityService.getAssignedToEdgeEntitiesByType(node, pageLink).subscribe(
284 129 (entities) => {
... ... @@ -289,6 +134,8 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
289 134 }
290 135 }
291 136 )
  137 + } else {
  138 + cb([]);
292 139 }
293 140 }
294 141
... ... @@ -303,11 +150,11 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
303 150 text: edgeGroupsNodeText(this.translateService, entityType),
304 151 children: true,
305 152 data: {
306   - type: 'edgeGroups',
  153 + type: 'edgeGroup',
307 154 entityType,
308 155 edge,
309 156 internalId: edge.id.id + '_' + entityType
310   - } as EdgeGroupsNodeData
  157 + } as EdgeGroupNodeData
311 158 };
312 159 nodes.push(node);
313 160 nodesMap[entityType] = node.id;
... ... @@ -330,10 +177,10 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
330 177 disabled: false
331 178 },
332 179 data: {
333   - type: 'edge',
  180 + type: 'entity',
334 181 entity: edge,
335 182 internalId: edge.id.id
336   - } as EdgeNodeData
  183 + } as EntityNodeData
337 184 };
338 185 nodesMap[edge.id.id] = node.id;
339 186 return node;
... ... @@ -351,94 +198,6 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
351 198 return nodes;
352 199 }
353 200
354   - public onNodeSelected: NodeSelectedCallback = (node, event) => {
355   - let nodeId;
356   - if (!node) {
357   - nodeId = -1;
358   - } else {
359   - nodeId = node.id;
360   - }
361   - if (nodeId !== -1) {
362   - const selectedNode = this.nodesMap[nodeId];
363   - if (selectedNode) {
364   - const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected');
365   - if (descriptors.length) {
366   - const entity = selectedNode.data.nodeCtx.entity;
367   - this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx });
368   - }
369   - }
370   - }
371   - }
372   -
373   - public onNodesInserted: NodesInsertedCallback = (nodes) => {
374   - if (nodes) {
375   - nodes.forEach((nodeId) => {
376   - const task = this.pendingUpdateNodeTasks[nodeId];
377   - if (task) {
378   - task();
379   - delete this.pendingUpdateNodeTasks[nodeId];
380   - }
381   - });
382   - }
383   - }
384   -
385   - public searchCallback: NodeSearchCallback = (searchText, node) => {
386   - const theNode = this.nodesMap[node.id];
387   - if (theNode && theNode.data.searchText) {
388   - return theNode.data.searchText.includes(searchText.toLowerCase());
389   - }
390   - return false;
391   - }
392   -
393   - private updateNodeStyle(node: HierarchyNavTreeNode) {
394   - const newText = this.prepareNodeText(node);
395   - if (node.text !== newText) {
396   - node.text = newText;
397   - this.nodeEditCallbacks.updateNode(node.id, node.text);
398   - }
399   - const newDisabled = this.nodeDisabledFunction(node.data.nodeCtx);
400   - if (node.state.disabled !== newDisabled) {
401   - node.state.disabled = newDisabled;
402   - if (node.state.disabled) {
403   - this.nodeEditCallbacks.disableNode(node.id);
404   - } else {
405   - this.nodeEditCallbacks.enableNode(node.id);
406   - }
407   - }
408   - const newHasChildren = this.nodeHasChildrenFunction(node.data.nodeCtx);
409   - if (node.children !== newHasChildren) {
410   - node.children = newHasChildren;
411   - this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children);
412   - }
413   - }
414   -
415   - private prepareNodeText(node: HierarchyNavTreeNode): string {
416   - const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx);
417   - const nodeText = this.nodeTextFunction(node.data.nodeCtx);
418   - node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : '';
419   - return nodeIcon + nodeText;
420   - }
421   -
422   - private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string {
423   - let iconInfo = this.nodeIconFunction(nodeCtx);
424   - if (iconInfo) {
425   - if (iconInfo === 'default') {
426   - iconInfo = defaultNodeIconFunction(nodeCtx);
427   - }
428   - if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) {
429   - if (iconInfo.materialIcon) {
430   - return materialIconHtml(iconInfo.materialIcon);
431   - } else {
432   - return iconUrlHtml(iconInfo.iconUrl);
433   - }
434   - } else {
435   - return '';
436   - }
437   - } else {
438   - return '';
439   - }
440   - }
441   -
442 201 private getCustomerTitle(edgeId) {
443 202 this.edgeService.getEdgeInfo(edgeId).subscribe(
444 203 (edge) => {
... ...
... ... @@ -14,83 +14,16 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { BaseData } from '@shared/models/base-data';
18   -import { EntityId } from '@shared/models/id/entity-id';
19   -import { NavTreeNode, NodesCallback } from '@shared/components/nav-tree.component';
  17 +import { NavTreeNode } from '@shared/components/nav-tree.component';
20 18 import { Datasource } from '@shared/models/widget.models';
21   -import { isDefined, isUndefined } from '@core/utils';
22   -import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models';
23 19 import { EntityType } from '@shared/models/entity-type.models';
24 20 import { Edge } from "@shared/models/edge.models";
25 21 import { TranslateService } from "@ngx-translate/core";
26 22
27   -export interface EdgesOverviewWidgetSettings {
28   - nodeRelationQueryFunction: string;
29   - nodeHasChildrenFunction: string;
30   - nodeOpenedFunction: string;
31   - nodeDisabledFunction: string;
32   - nodeIconFunction: string;
33   - nodeTextFunction: string;
34   - nodesSortFunction: string;
35   -}
36   -
37   -export interface HierarchyNodeContext {
38   - parentNodeCtx?: HierarchyNodeContext;
39   - entity: BaseData<EntityId>;
40   - childrenNodesLoaded?: boolean;
41   - level?: number;
42   - data: {[key: string]: any};
43   -}
44   -
45   -export interface HierarchyNavTreeNode extends NavTreeNode {
46   - data?: {
47   - datasource: HierarchyNodeDatasource;
48   - nodeCtx: HierarchyNodeContext;
49   - searchText?: string;
50   - };
51   -}
52   -
53   -export interface HierarchyNodeDatasource extends Datasource {
  23 +export interface EntityNodeDatasource extends Datasource {
54 24 nodeId: string;
55 25 }
56 26
57   -export interface HierarchyNodeIconInfo {
58   - iconUrl?: string;
59   - materialIcon?: string;
60   -}
61   -
62   -export type NodeRelationQueryFunction = (nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default';
63   -export type NodeTextFunction = (nodeCtx: HierarchyNodeContext) => string;
64   -export type NodeDisabledFunction = (nodeCtx: HierarchyNodeContext) => boolean;
65   -export type NodeIconFunction = (nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default';
66   -export type NodeOpenedFunction = (nodeCtx: HierarchyNodeContext) => boolean;
67   -export type NodeHasChildrenFunction = (nodeCtx: HierarchyNodeContext) => boolean;
68   -export type NodesSortFunction = (nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number;
69   -
70   -export function loadNodeCtxFunction<F extends (...args: any[]) => any>(functionBody: string, argNames: string, ...args: any[]): F {
71   - let nodeCtxFunction: F = null;
72   - if (isDefined(functionBody) && functionBody.length) {
73   - try {
74   - nodeCtxFunction = new Function(argNames, functionBody) as F;
75   - const res = nodeCtxFunction.apply(null, args);
76   - if (isUndefined(res)) {
77   - nodeCtxFunction = null;
78   - }
79   - } catch (e) {
80   - nodeCtxFunction = null;
81   - }
82   - }
83   - return nodeCtxFunction;
84   -}
85   -
86   -export function materialIconHtml(materialIcon: string): string {
87   - return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
88   -}
89   -
90   -export function iconUrlHtml(iconUrl: string): string {
91   - return '<div class="node-icon" style="background-image: url(' + iconUrl + ');">&nbsp;</div>';
92   -}
93   -
94 27 export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string {
95 28 const nodeIcon = materialIconByEntityType(entityType);
96 29 const nodeText = textForEdgeGroupsType(translate, entityType);
... ... @@ -112,12 +45,6 @@ export function materialIconByEntityType(entityType: EntityType): string {
112 45 case EntityType.ASSET:
113 46 materialIcon = 'domain';
114 47 break;
115   - case EntityType.CUSTOMER:
116   - materialIcon = 'supervisor_account';
117   - break;
118   - case EntityType.USER:
119   - materialIcon = 'account_circle';
120   - break;
121 48 case EntityType.DASHBOARD:
122 49 materialIcon = 'dashboards';
123 50 break;
... ... @@ -127,9 +54,6 @@ export function materialIconByEntityType(entityType: EntityType): string {
127 54 case EntityType.RULE_CHAIN:
128 55 materialIcon = 'settings_ethernet';
129 56 break;
130   - case EntityType.EDGE:
131   - materialIcon = 'router';
132   - break;
133 57 }
134 58 return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
135 59 }
... ... @@ -156,26 +80,6 @@ export function textForEdgeGroupsType(translate: TranslateService, entityType: E
156 80 return translate.instant(textForEdgeGroupsType);
157 81 }
158 82
159   -export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = nodeCtx => {
160   - const entity = nodeCtx.entity;
161   - const query: EntityRelationsQuery = {
162   - parameters: {
163   - rootId: entity.id.id,
164   - rootType: entity.id.entityType as EntityType,
165   - direction: EntitySearchDirection.FROM,
166   - relationTypeGroup: RelationTypeGroup.COMMON,
167   - maxLevel: 1
168   - },
169   - filters: [
170   - {
171   - relationType: 'Contains',
172   - entityTypes: []
173   - }
174   - ]
175   - };
176   - return query;
177   -};
178   -
179 83 export const edgeGroupsTypes: EntityType[] = [
180 84 EntityType.ASSET,
181 85 EntityType.DEVICE,
... ... @@ -184,71 +88,20 @@ export const edgeGroupsTypes: EntityType[] = [
184 88 EntityType.RULE_CHAIN
185 89 ]
186 90
187   -export const defaultNodeIconFunction: NodeIconFunction = nodeCtx => {
188   - let materialIcon = 'insert_drive_file';
189   - const entity = nodeCtx.entity;
190   - if (entity && entity.id && entity.id.entityType) {
191   - switch (entity.id.entityType as EntityType | string) {
192   - case 'function':
193   - materialIcon = 'functions';
194   - break;
195   - case EntityType.DEVICE:
196   - materialIcon = 'devices_other';
197   - break;
198   - case EntityType.ASSET:
199   - materialIcon = 'domain';
200   - break;
201   - case EntityType.TENANT:
202   - materialIcon = 'supervisor_account';
203   - break;
204   - case EntityType.CUSTOMER:
205   - materialIcon = 'supervisor_account';
206   - break;
207   - case EntityType.USER:
208   - materialIcon = 'account_circle';
209   - break;
210   - case EntityType.DASHBOARD:
211   - materialIcon = 'dashboards';
212   - break;
213   - case EntityType.ALARM:
214   - materialIcon = 'notifications_active';
215   - break;
216   - case EntityType.ENTITY_VIEW:
217   - materialIcon = 'view_quilt';
218   - break;
219   - }
220   - }
221   - return {
222   - materialIcon
223   - };
224   -};
225   -
226   -export const defaultNodeOpenedFunction: NodeOpenedFunction = nodeCtx => {
227   - return nodeCtx.level <= 4;
228   -};
229   -
230   -export const defaultNodesSortFunction: NodesSortFunction = (nodeCtx1, nodeCtx2) => {
231   - let result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);
232   - if (result === 0) {
233   - result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);
234   - }
235   - return result;
236   -};
237   -
238 91 export interface EdgeOverviewNode extends NavTreeNode {
239 92 data?: EdgeOverviewNodeData;
240 93 }
241 94
242   -export type EdgeOverviewNodeData = EdgeGroupsNodeData | EdgeNodeData;
  95 +export type EdgeOverviewNodeData = EdgeGroupNodeData | EntityNodeData;
243 96
244   -export interface EdgeGroupsNodeData extends BaseEdgeOverviewNodeData {
245   - type: 'edgeGroups';
  97 +export interface EdgeGroupNodeData extends BaseEdgeOverviewNodeData {
  98 + type: 'edgeGroup';
246 99 entityType: EntityType;
247 100 edge: Edge;
248 101 }
249 102
250   -export interface EdgeNodeData extends BaseEdgeOverviewNodeData {
251   - type: 'edge';
  103 +export interface EntityNodeData extends BaseEdgeOverviewNodeData {
  104 + type: 'entity';
252 105 entity: Edge;
253 106 }
254 107
... ... @@ -257,4 +110,4 @@ export interface BaseEdgeOverviewNodeData {
257 110 internalId: string;
258 111 }
259 112
260   -export type EdgeOverviewNodeType = 'edge' | 'edgeGroups';
  113 +export type EdgeOverviewNodeType = 'entity' | 'edgeGroup';
... ...