Commit 02b5462720b7b2c58a2af9126ad0f6fa6b20deb5
Merge branch 'develop/3.3-edge' of github.com:volodymyr-babak/thingsboard into develop/3.3-edge
Showing
11 changed files
with
531 additions
and
5 deletions
1 | +{ | ||
2 | + "widgetsBundle": { | ||
3 | + "alias": "edge_widgets", | ||
4 | + "title": "Edge widgets", | ||
5 | + "image": null | ||
6 | + }, | ||
7 | + "widgetTypes": [ | ||
8 | + { | ||
9 | + "alias": "edges_overview", | ||
10 | + "name": "Edges Quick Overview", | ||
11 | + "descriptor": { | ||
12 | + "type": "latest", | ||
13 | + "sizeX": 7.5, | ||
14 | + "sizeY": 5, | ||
15 | + "resources": [], | ||
16 | + "templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>", | ||
17 | + "templateCss": "", | ||
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 | + } | ||
23 | + } | ||
24 | + ] | ||
25 | +} |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
439 | this.deleteSystemWidgetBundle("input_widgets"); | 439 | this.deleteSystemWidgetBundle("input_widgets"); |
440 | this.deleteSystemWidgetBundle("date"); | 440 | this.deleteSystemWidgetBundle("date"); |
441 | this.deleteSystemWidgetBundle("entity_admin_widgets"); | 441 | this.deleteSystemWidgetBundle("entity_admin_widgets"); |
442 | + this.deleteSystemWidgetBundle("edge_widgets"); | ||
442 | installScripts.loadSystemWidgets(); | 443 | installScripts.loadSystemWidgets(); |
443 | } | 444 | } |
444 | 445 |
@@ -1157,4 +1157,26 @@ export class EntityService { | @@ -1157,4 +1157,26 @@ export class EntityService { | ||
1157 | datasource.dataKeys.push(dataKey); | 1157 | datasource.dataKeys.push(dataKey); |
1158 | }); | 1158 | }); |
1159 | } | 1159 | } |
1160 | + | ||
1161 | + public getAssignedToEdgeEntitiesByType(edgeId: string, entityType: EntityType, pageLink: PageLink): Observable<PageData<any>> { | ||
1162 | + let entitiesObservable: Observable<PageData<any>>; | ||
1163 | + switch (entityType) { | ||
1164 | + case (EntityType.ASSET): | ||
1165 | + entitiesObservable = this.assetService.getEdgeAssets(edgeId, pageLink); | ||
1166 | + break; | ||
1167 | + case (EntityType.DEVICE): | ||
1168 | + entitiesObservable = this.deviceService.getEdgeDevices(edgeId, pageLink); | ||
1169 | + break; | ||
1170 | + case (EntityType.ENTITY_VIEW): | ||
1171 | + entitiesObservable = this.entityViewService.getEdgeEntityViews(edgeId, pageLink); | ||
1172 | + break; | ||
1173 | + case (EntityType.DASHBOARD): | ||
1174 | + entitiesObservable = this.dashboardService.getEdgeDashboards(edgeId, pageLink); | ||
1175 | + break; | ||
1176 | + case (EntityType.RULE_CHAIN): | ||
1177 | + entitiesObservable = this.ruleChainService.getEdgeRuleChains(edgeId, pageLink); | ||
1178 | + break; | ||
1179 | + } | ||
1180 | + return entitiesObservable; | ||
1181 | + } | ||
1160 | } | 1182 | } |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<div class="tb-edges-overview tb-absolute-fill" tb-toast toastTarget="{{ toastTargetId }}"> | ||
19 | + <div fxFlex fxLayout="column" class="tb-absolute-fill"> | ||
20 | + <span class="mat-subheader" *ngIf="customerTitle" fxLayout="row">{{ customerTitle }}</span> | ||
21 | + <span class="mat-subheader" *ngIf="!edgeIsDatasource" fxLayout="row" translate>edge.widget-datasource-error</span> | ||
22 | + <div fxFlex class="tb-entities-nav-tree-panel"> | ||
23 | + <tb-nav-tree | ||
24 | + [loadNodes]="loadNodes" | ||
25 | + ></tb-nav-tree> | ||
26 | + </div> | ||
27 | + </div> | ||
28 | +</div> |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +:host-context(.tb-has-timewindow) { | ||
17 | + .tb-edges-overview { | ||
18 | + mat-toolbar { | ||
19 | + height: 60px; | ||
20 | + max-height: 60px; | ||
21 | + .mat-toolbar-tools { | ||
22 | + height: 60px; | ||
23 | + max-height: 60px; | ||
24 | + } | ||
25 | + } | ||
26 | + } | ||
27 | +} | ||
28 | + | ||
29 | +:host { | ||
30 | + .tb-edges-overview { | ||
31 | + mat-toolbar.mat-table-toolbar:not([color="primary"]) { | ||
32 | + background: transparent; | ||
33 | + } | ||
34 | + mat-toolbar { | ||
35 | + min-height: 39px; | ||
36 | + max-height: 39px; | ||
37 | + .mat-toolbar-tools { | ||
38 | + min-height: 39px; | ||
39 | + max-height: 39px; | ||
40 | + } | ||
41 | + } | ||
42 | + | ||
43 | + .mat-subheader { | ||
44 | + padding: 15px; | ||
45 | + color: rgba(0,0,0,0.54); | ||
46 | + font-size: 14px; | ||
47 | + font-weight: 400; | ||
48 | + } | ||
49 | + | ||
50 | + .tb-entities-nav-tree-panel { | ||
51 | + overflow-x: auto; | ||
52 | + overflow-y: auto; | ||
53 | + } | ||
54 | + } | ||
55 | +} | ||
56 | + | ||
57 | +:host ::ng-deep { | ||
58 | + .tb-nav-tree-container { | ||
59 | + &.jstree-proton { | ||
60 | + .jstree-anchor { | ||
61 | + div.node-icon { | ||
62 | + display: inline-block; | ||
63 | + width: 22px; | ||
64 | + height: 22px; | ||
65 | + margin-right: 2px; | ||
66 | + margin-bottom: 2px; | ||
67 | + background-color: transparent; | ||
68 | + background-repeat: no-repeat; | ||
69 | + background-attachment: scroll; | ||
70 | + background-position: center center; | ||
71 | + background-size: 18px 18px; | ||
72 | + } | ||
73 | + | ||
74 | + mat-icon.node-icon { | ||
75 | + width: 22px; | ||
76 | + min-width: 22px; | ||
77 | + height: 22px; | ||
78 | + min-height: 22px; | ||
79 | + margin-right: 2px; | ||
80 | + margin-bottom: 2px; | ||
81 | + color: inherit; | ||
82 | + vertical-align: middle; | ||
83 | + | ||
84 | + &.material-icons { | ||
85 | + font-size: 18px; | ||
86 | + line-height: 22px; | ||
87 | + text-align: center; | ||
88 | + } | ||
89 | + } | ||
90 | + | ||
91 | + &.jstree-hovered:not(.jstree-clicked), | ||
92 | + &.jstree-disabled { | ||
93 | + div.node-icon { | ||
94 | + opacity: .5; | ||
95 | + } | ||
96 | + } | ||
97 | + } | ||
98 | + } | ||
99 | + } | ||
100 | + | ||
101 | + @media (max-width: 768px) { | ||
102 | + .tb-nav-tree-container { | ||
103 | + &.jstree-proton-responsive { | ||
104 | + .jstree-anchor { | ||
105 | + div.node-icon { | ||
106 | + width: 40px; | ||
107 | + height: 40px; | ||
108 | + margin: 0; | ||
109 | + background-size: 24px 24px; | ||
110 | + } | ||
111 | + | ||
112 | + mat-icon.node-icon { | ||
113 | + width: 40px; | ||
114 | + min-width: 40px; | ||
115 | + height: 40px; | ||
116 | + min-height: 40px; | ||
117 | + margin: 0; | ||
118 | + | ||
119 | + &.material-icons { | ||
120 | + font-size: 24px; | ||
121 | + line-height: 40px; | ||
122 | + } | ||
123 | + } | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | + } | ||
128 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; | ||
18 | +import { PageComponent } from '@shared/components/page.component'; | ||
19 | +import { Store } from '@ngrx/store'; | ||
20 | +import { AppState } from '@core/core.state'; | ||
21 | +import { WidgetContext } from '@home/models/widget-component.models'; | ||
22 | +import { Datasource, DatasourceType, WidgetConfig } from '@shared/models/widget.models'; | ||
23 | +import { IWidgetSubscription } from '@core/api/widget-api.models'; | ||
24 | +import { UtilsService } from '@core/services/utils.service'; | ||
25 | +import { LoadNodesCallback } from '@shared/components/nav-tree.component'; | ||
26 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
27 | +import { | ||
28 | + EdgeGroupNodeData, | ||
29 | + edgeGroupsNodeText, | ||
30 | + edgeGroupsTypes, | ||
31 | + entityNodeText, | ||
32 | + EdgeOverviewNode, | ||
33 | + EntityNodeData, | ||
34 | + EntityNodeDatasource | ||
35 | +} from '@home/components/widget/lib/edges-overview-widget.models'; | ||
36 | +import { EdgeService } from "@core/http/edge.service"; | ||
37 | +import { EntityService } from "@core/http/entity.service"; | ||
38 | +import { TranslateService } from "@ngx-translate/core"; | ||
39 | +import { PageLink } from "@shared/models/page/page-link"; | ||
40 | +import { BaseData, HasId } from "@shared/models/base-data"; | ||
41 | +import { EntityId } from "@shared/models/id/entity-id"; | ||
42 | +import { getCurrentAuthUser } from "@core/auth/auth.selectors"; | ||
43 | +import { Authority } from "@shared/models/authority.enum"; | ||
44 | + | ||
45 | +@Component({ | ||
46 | + selector: 'tb-edges-overview-widget', | ||
47 | + templateUrl: './edges-overview-widget.component.html', | ||
48 | + styleUrls: ['./edges-overview-widget.component.scss'] | ||
49 | +}) | ||
50 | +export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit { | ||
51 | + | ||
52 | + @Input() | ||
53 | + ctx: WidgetContext; | ||
54 | + | ||
55 | + public toastTargetId = 'edges-overview-' + this.utils.guid(); | ||
56 | + public customerTitle: string = null; | ||
57 | + public edgeIsDatasource: boolean = true; | ||
58 | + | ||
59 | + private widgetConfig: WidgetConfig; | ||
60 | + private subscription: IWidgetSubscription; | ||
61 | + private datasources: Array<EntityNodeDatasource>; | ||
62 | + | ||
63 | + private nodeIdCounter = 0; | ||
64 | + | ||
65 | + private entityNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {}; | ||
66 | + private entityGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {}; | ||
67 | + | ||
68 | + constructor(protected store: Store<AppState>, | ||
69 | + private edgeService: EdgeService, | ||
70 | + private entityService: EntityService, | ||
71 | + private translateService: TranslateService, | ||
72 | + private utils: UtilsService, | ||
73 | + private cd: ChangeDetectorRef) { | ||
74 | + super(store); | ||
75 | + } | ||
76 | + | ||
77 | + ngOnInit(): void { | ||
78 | + this.widgetConfig = this.ctx.widgetConfig; | ||
79 | + this.subscription = this.ctx.defaultSubscription; | ||
80 | + this.datasources = this.subscription.datasources as Array<EntityNodeDatasource>; | ||
81 | + this.ctx.updateWidgetParams(); | ||
82 | + } | ||
83 | + | ||
84 | + public loadNodes: LoadNodesCallback = (node, cb) => { | ||
85 | + const datasource: Datasource = this.datasources[0]; | ||
86 | + if (node.id === '#' && datasource) { | ||
87 | + if (datasource.type === DatasourceType.entity && datasource.entity.id.entityType === EntityType.EDGE) { | ||
88 | + var selectedEdge: BaseData<EntityId> = datasource.entity; | ||
89 | + this.getCustomerTitle(selectedEdge.id.id); | ||
90 | + this.ctx.widgetTitle = selectedEdge.name; | ||
91 | + cb(this.loadNodesForEdge(selectedEdge.id.id, selectedEdge)); | ||
92 | + } else if (datasource.type === DatasourceType.function) { | ||
93 | + cb(this.loadNodesForEdge(datasource.entityId, datasource.entity)); | ||
94 | + } else { | ||
95 | + this.edgeIsDatasource = false; | ||
96 | + cb([]); | ||
97 | + } | ||
98 | + } | ||
99 | + else if (node.data && node.data.entity.id.entityType === EntityType.EDGE) { | ||
100 | + const edgeId = node.data.entity.id.id; | ||
101 | + const entityType = node.data.entityType; | ||
102 | + const pageLink = new PageLink(datasource.pageLink.pageSize); | ||
103 | + this.entityService.getAssignedToEdgeEntitiesByType(edgeId, entityType, pageLink).subscribe( | ||
104 | + (entities) => { | ||
105 | + if (entities.data.length > 0) { | ||
106 | + cb(this.entitiesToNodes(node.id, entities.data)); | ||
107 | + } else { | ||
108 | + cb([]); | ||
109 | + } | ||
110 | + } | ||
111 | + ) | ||
112 | + } else { | ||
113 | + cb([]); | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + private loadNodesForEdge(parentNodeId: string, entity: BaseData<HasId>): EdgeOverviewNode[] { | ||
118 | + const nodes: EdgeOverviewNode[] = []; | ||
119 | + const nodesMap = {}; | ||
120 | + this.entityGroupsNodesMap[parentNodeId] = nodesMap; | ||
121 | + const authUser = getCurrentAuthUser(this.store); | ||
122 | + var allowedGroupTypes: EntityType[] = edgeGroupsTypes; | ||
123 | + if (authUser.authority === Authority.CUSTOMER_USER) { | ||
124 | + allowedGroupTypes = edgeGroupsTypes.filter(type => type !== EntityType.RULE_CHAIN); | ||
125 | + } | ||
126 | + allowedGroupTypes.forEach((entityType) => { | ||
127 | + const node: EdgeOverviewNode = { | ||
128 | + id: (++this.nodeIdCounter)+'', | ||
129 | + icon: false, | ||
130 | + text: edgeGroupsNodeText(this.translateService, entityType), | ||
131 | + children: true, | ||
132 | + data: { | ||
133 | + entityType, | ||
134 | + entity, | ||
135 | + internalId: entity.id.id + '_' + entityType | ||
136 | + } as EdgeGroupNodeData | ||
137 | + }; | ||
138 | + nodes.push(node); | ||
139 | + nodesMap[entityType] = node.id; | ||
140 | + }); | ||
141 | + return nodes; | ||
142 | + } | ||
143 | + | ||
144 | + private createEntityNode(parentNodeId: string, entity: BaseData<HasId>): EdgeOverviewNode { | ||
145 | + let nodesMap = this.entityNodesMap[parentNodeId]; | ||
146 | + if (!nodesMap) { | ||
147 | + nodesMap = {}; | ||
148 | + this.entityNodesMap[parentNodeId] = nodesMap; | ||
149 | + } | ||
150 | + const node: EdgeOverviewNode = { | ||
151 | + id: (++this.nodeIdCounter)+'', | ||
152 | + icon: false, | ||
153 | + text: entityNodeText(entity), | ||
154 | + children: false, | ||
155 | + state: { | ||
156 | + disabled: false | ||
157 | + }, | ||
158 | + data: { | ||
159 | + entity: entity, | ||
160 | + internalId: entity.id.id | ||
161 | + } as EntityNodeData | ||
162 | + }; | ||
163 | + nodesMap[entity.id.id] = node.id; | ||
164 | + return node; | ||
165 | + } | ||
166 | + | ||
167 | + private entitiesToNodes(parentNodeId: string, entities: BaseData<HasId>[]): EdgeOverviewNode[] { | ||
168 | + const nodes: EdgeOverviewNode[] = []; | ||
169 | + this.entityNodesMap[parentNodeId] = {}; | ||
170 | + if (entities) { | ||
171 | + entities.forEach((entity) => { | ||
172 | + const node = this.createEntityNode(parentNodeId, entity); | ||
173 | + nodes.push(node); | ||
174 | + }); | ||
175 | + } | ||
176 | + return nodes; | ||
177 | + } | ||
178 | + | ||
179 | + private getCustomerTitle(edgeId: string) { | ||
180 | + this.edgeService.getEdgeInfo(edgeId).subscribe( | ||
181 | + (edge) => { | ||
182 | + if (edge.customerTitle) { | ||
183 | + this.customerTitle = this.translateService.instant('edge.assigned-to-customer', {customerTitle: edge.customerTitle}); | ||
184 | + } else { | ||
185 | + this.customerTitle = null; | ||
186 | + } | ||
187 | + this.cd.detectChanges(); | ||
188 | + }); | ||
189 | + } | ||
190 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { NavTreeNode } from '@shared/components/nav-tree.component'; | ||
18 | +import { Datasource } from '@shared/models/widget.models'; | ||
19 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
20 | +import { TranslateService } from "@ngx-translate/core"; | ||
21 | +import { BaseData, HasId } from "@shared/models/base-data"; | ||
22 | + | ||
23 | +export interface EntityNodeDatasource extends Datasource { | ||
24 | + nodeId: string; | ||
25 | +} | ||
26 | + | ||
27 | +export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string { | ||
28 | + const nodeIcon = materialIconByEntityType(entityType); | ||
29 | + const nodeText = textForEdgeGroupsType(translate, entityType); | ||
30 | + return nodeIcon + nodeText; | ||
31 | +} | ||
32 | + | ||
33 | +export function entityNodeText(entity: any): string { | ||
34 | + const nodeIcon = materialIconByEntityType(entity.id.entityType); | ||
35 | + const nodeText = entity.name; | ||
36 | + return nodeIcon + nodeText; | ||
37 | +} | ||
38 | + | ||
39 | +export function materialIconByEntityType(entityType: EntityType): string { | ||
40 | + let materialIcon = 'insert_drive_file'; | ||
41 | + switch (entityType) { | ||
42 | + case EntityType.DEVICE: | ||
43 | + materialIcon = 'devices_other'; | ||
44 | + break; | ||
45 | + case EntityType.ASSET: | ||
46 | + materialIcon = 'domain'; | ||
47 | + break; | ||
48 | + case EntityType.DASHBOARD: | ||
49 | + materialIcon = 'dashboards'; | ||
50 | + break; | ||
51 | + case EntityType.ENTITY_VIEW: | ||
52 | + materialIcon = 'view_quilt'; | ||
53 | + break; | ||
54 | + case EntityType.RULE_CHAIN: | ||
55 | + materialIcon = 'settings_ethernet'; | ||
56 | + break; | ||
57 | + } | ||
58 | + return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>'; | ||
59 | +} | ||
60 | + | ||
61 | +export function textForEdgeGroupsType(translate: TranslateService, entityType: EntityType): string { | ||
62 | + let textForEdgeGroupsType: string = ''; | ||
63 | + switch (entityType) { | ||
64 | + case EntityType.DEVICE: | ||
65 | + textForEdgeGroupsType = 'device.devices'; | ||
66 | + break; | ||
67 | + case EntityType.ASSET: | ||
68 | + textForEdgeGroupsType = 'asset.assets'; | ||
69 | + break; | ||
70 | + case EntityType.DASHBOARD: | ||
71 | + textForEdgeGroupsType = 'dashboard.dashboards'; | ||
72 | + break; | ||
73 | + case EntityType.ENTITY_VIEW: | ||
74 | + textForEdgeGroupsType = 'entity-view.entity-views'; | ||
75 | + break; | ||
76 | + case EntityType.RULE_CHAIN: | ||
77 | + textForEdgeGroupsType = 'rulechain.rulechains'; | ||
78 | + break; | ||
79 | + } | ||
80 | + return translate.instant(textForEdgeGroupsType); | ||
81 | +} | ||
82 | + | ||
83 | +export const edgeGroupsTypes: EntityType[] = [ | ||
84 | + EntityType.ASSET, | ||
85 | + EntityType.DEVICE, | ||
86 | + EntityType.ENTITY_VIEW, | ||
87 | + EntityType.DASHBOARD, | ||
88 | + EntityType.RULE_CHAIN | ||
89 | +] | ||
90 | + | ||
91 | +export interface EdgeOverviewNode extends NavTreeNode { | ||
92 | + data?: EdgeOverviewNodeData; | ||
93 | +} | ||
94 | + | ||
95 | +export type EdgeOverviewNodeData = EdgeGroupNodeData | EntityNodeData; | ||
96 | + | ||
97 | +export interface EdgeGroupNodeData extends BaseEdgeOverviewNodeData { | ||
98 | + entityType: EntityType; | ||
99 | + entity: BaseData<HasId>; | ||
100 | +} | ||
101 | + | ||
102 | +export interface EntityNodeData extends BaseEdgeOverviewNodeData { | ||
103 | + entity: BaseData<HasId>; | ||
104 | +} | ||
105 | + | ||
106 | +export interface BaseEdgeOverviewNodeData { | ||
107 | + internalId: string; | ||
108 | +} |
@@ -35,6 +35,7 @@ import { TripAnimationComponent } from './trip-animation/trip-animation.componen | @@ -35,6 +35,7 @@ import { TripAnimationComponent } from './trip-animation/trip-animation.componen | ||
35 | import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component'; | 35 | import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component'; |
36 | import { GatewayFormComponent } from './lib/gateway/gateway-form.component'; | 36 | import { GatewayFormComponent } from './lib/gateway/gateway-form.component'; |
37 | import { ImportExportService } from '@home/components/import-export/import-export.service'; | 37 | import { ImportExportService } from '@home/components/import-export/import-export.service'; |
38 | +import { EdgesOverviewWidgetComponent } from "@home/components/widget/lib/edges-overview-widget.component"; | ||
38 | 39 | ||
39 | @NgModule({ | 40 | @NgModule({ |
40 | declarations: | 41 | declarations: |
@@ -45,6 +46,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor | @@ -45,6 +46,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor | ||
45 | AlarmsTableWidgetComponent, | 46 | AlarmsTableWidgetComponent, |
46 | TimeseriesTableWidgetComponent, | 47 | TimeseriesTableWidgetComponent, |
47 | EntitiesHierarchyWidgetComponent, | 48 | EntitiesHierarchyWidgetComponent, |
49 | + EdgesOverviewWidgetComponent, | ||
48 | DateRangeNavigatorWidgetComponent, | 50 | DateRangeNavigatorWidgetComponent, |
49 | DateRangeNavigatorPanelComponent, | 51 | DateRangeNavigatorPanelComponent, |
50 | MultipleInputWidgetComponent, | 52 | MultipleInputWidgetComponent, |
@@ -63,6 +65,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor | @@ -63,6 +65,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor | ||
63 | AlarmsTableWidgetComponent, | 65 | AlarmsTableWidgetComponent, |
64 | TimeseriesTableWidgetComponent, | 66 | TimeseriesTableWidgetComponent, |
65 | EntitiesHierarchyWidgetComponent, | 67 | EntitiesHierarchyWidgetComponent, |
68 | + EdgesOverviewWidgetComponent, | ||
66 | RpcWidgetsModule, | 69 | RpcWidgetsModule, |
67 | DateRangeNavigatorWidgetComponent, | 70 | DateRangeNavigatorWidgetComponent, |
68 | MultipleInputWidgetComponent, | 71 | MultipleInputWidgetComponent, |
@@ -33,11 +33,12 @@ import { WidgetsBundleComponent } from '@modules/home/pages/widget/widgets-bundl | @@ -33,11 +33,12 @@ import { WidgetsBundleComponent } from '@modules/home/pages/widget/widgets-bundl | ||
33 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 33 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
34 | import { Store } from '@ngrx/store'; | 34 | import { Store } from '@ngrx/store'; |
35 | import { AppState } from '@core/core.state'; | 35 | import { AppState } from '@core/core.state'; |
36 | -import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; | 36 | +import { getCurrentAuthState, getCurrentAuthUser } from '@app/core/auth/auth.selectors'; |
37 | import { Authority } from '@shared/models/authority.enum'; | 37 | import { Authority } from '@shared/models/authority.enum'; |
38 | import { DialogService } from '@core/services/dialog.service'; | 38 | import { DialogService } from '@core/services/dialog.service'; |
39 | import { ImportExportService } from '@home/components/import-export/import-export.service'; | 39 | import { ImportExportService } from '@home/components/import-export/import-export.service'; |
40 | import { Direction } from "@shared/models/page/sort-order"; | 40 | import { Direction } from "@shared/models/page/sort-order"; |
41 | +import { map } from "rxjs/operators"; | ||
41 | 42 | ||
42 | @Injectable() | 43 | @Injectable() |
43 | export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> { | 44 | export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> { |
@@ -106,7 +107,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon | @@ -106,7 +107,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon | ||
106 | this.config.deleteEntitiesTitle = count => this.translate.instant('widgets-bundle.delete-widgets-bundles-title', {count}); | 107 | this.config.deleteEntitiesTitle = count => this.translate.instant('widgets-bundle.delete-widgets-bundles-title', {count}); |
107 | this.config.deleteEntitiesContent = () => this.translate.instant('widgets-bundle.delete-widgets-bundles-text'); | 108 | this.config.deleteEntitiesContent = () => this.translate.instant('widgets-bundle.delete-widgets-bundles-text'); |
108 | 109 | ||
109 | - this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink); | 110 | + |
110 | this.config.loadEntity = id => this.widgetsService.getWidgetsBundle(id.id); | 111 | this.config.loadEntity = id => this.widgetsService.getWidgetsBundle(id.id); |
111 | this.config.saveEntity = widgetsBundle => this.widgetsService.saveWidgetsBundle(widgetsBundle); | 112 | this.config.saveEntity = widgetsBundle => this.widgetsService.saveWidgetsBundle(widgetsBundle); |
112 | this.config.deleteEntity = id => this.widgetsService.deleteWidgetsBundle(id.id); | 113 | this.config.deleteEntity = id => this.widgetsService.deleteWidgetsBundle(id.id); |
@@ -119,6 +120,17 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon | @@ -119,6 +120,17 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon | ||
119 | this.config.deleteEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); | 120 | this.config.deleteEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); |
120 | this.config.entitySelectionEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); | 121 | this.config.entitySelectionEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); |
121 | this.config.detailsReadonly = (widgetsBundle) => !this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); | 122 | this.config.detailsReadonly = (widgetsBundle) => !this.isWidgetsBundleEditable(widgetsBundle, authUser.authority); |
123 | + const authState = getCurrentAuthState(this.store); | ||
124 | + if (!authState.edgesSupportEnabled) { | ||
125 | + this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink).pipe( | ||
126 | + map((widgetBundles) => { | ||
127 | + widgetBundles.data = widgetBundles.data.filter(widgetBundle => widgetBundle.alias !== 'edge_widgets'); | ||
128 | + return widgetBundles; | ||
129 | + }) | ||
130 | + ); | ||
131 | + } else { | ||
132 | + this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink); | ||
133 | + } | ||
122 | return this.config; | 134 | return this.config; |
123 | } | 135 | } |
124 | 136 |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; | 17 | import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; |
18 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; | 18 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
19 | import { Observable } from 'rxjs'; | 19 | import { Observable } from 'rxjs'; |
20 | -import { share, tap } from 'rxjs/operators'; | 20 | +import { map, share, tap } from 'rxjs/operators'; |
21 | import { Store } from '@ngrx/store'; | 21 | import { Store } from '@ngrx/store'; |
22 | import { AppState } from '@app/core/core.state'; | 22 | import { AppState } from '@app/core/core.state'; |
23 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 23 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
@@ -25,6 +25,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | @@ -25,6 +25,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | ||
25 | import { WidgetService } from '@core/http/widget.service'; | 25 | import { WidgetService } from '@core/http/widget.service'; |
26 | import { isDefined } from '@core/utils'; | 26 | import { isDefined } from '@core/utils'; |
27 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 27 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
28 | +import { getCurrentAuthState } from "@core/auth/auth.selectors"; | ||
28 | 29 | ||
29 | @Component({ | 30 | @Component({ |
30 | selector: 'tb-widgets-bundle-select', | 31 | selector: 'tb-widgets-bundle-select', |
@@ -94,6 +95,13 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni | @@ -94,6 +95,13 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni | ||
94 | } | 95 | } |
95 | } | 96 | } |
96 | }), | 97 | }), |
98 | + map((widgetsBundles) => { | ||
99 | + const authState = getCurrentAuthState(this.store); | ||
100 | + if (!authState.edgesSupportEnabled) { | ||
101 | + widgetsBundles = widgetsBundles.filter(widgetsBundle => widgetsBundle.alias !== 'edge_widgets'); | ||
102 | + } | ||
103 | + return widgetsBundles; | ||
104 | + }), | ||
97 | share() | 105 | share() |
98 | ); | 106 | ); |
99 | } | 107 | } |
@@ -1233,7 +1233,7 @@ | @@ -1233,7 +1233,7 @@ | ||
1233 | "assign-to-customer-text": "Please select the customer to assign the edge(s)", | 1233 | "assign-to-customer-text": "Please select the customer to assign the edge(s)", |
1234 | "assign-edge-to-customer": "Assign Edge(s) To Customer", | 1234 | "assign-edge-to-customer": "Assign Edge(s) To Customer", |
1235 | "assign-edge-to-customer-text": "Please select the edges to assign to the customer", | 1235 | "assign-edge-to-customer-text": "Please select the edges to assign to the customer", |
1236 | - "assigned-to-customer": "Assigned to customer", | 1236 | + "assigned-to-customer": "Assigned to: {{customerTitle}}", |
1237 | "unassign-from-customer": "Unassign from customer", | 1237 | "unassign-from-customer": "Unassign from customer", |
1238 | "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer", | 1238 | "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer", |
1239 | "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?", | 1239 | "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?", |
@@ -1287,7 +1287,8 @@ | @@ -1287,7 +1287,8 @@ | ||
1287 | "no-downlinks-prompt": "No downlinks found", | 1287 | "no-downlinks-prompt": "No downlinks found", |
1288 | "sync-process-started-successfully": "Sync process started successfully!", | 1288 | "sync-process-started-successfully": "Sync process started successfully!", |
1289 | "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", | 1289 | "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", |
1290 | - "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. <br><br> List of missing rule chain(s): <br> {{missingRuleChains}}" | 1290 | + "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. <br><br> List of missing rule chain(s): <br> {{missingRuleChains}}", |
1291 | + "widget-datasource-error": "This widget supports only EDGE entity datasource" | ||
1291 | }, | 1292 | }, |
1292 | "edge-event": { | 1293 | "edge-event": { |
1293 | "type-dashboard": "Dashboard", | 1294 | "type-dashboard": "Dashboard", |