Commit 02b5462720b7b2c58a2af9126ad0f6fa6b20deb5

Authored by Volodymyr Babak
2 parents c3e1eee6 1b490bf5

Merge branch 'develop/3.3-edge' of github.com:volodymyr-babak/thingsboard into develop/3.3-edge

  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 +}
... ...
... ... @@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
439 439 this.deleteSystemWidgetBundle("input_widgets");
440 440 this.deleteSystemWidgetBundle("date");
441 441 this.deleteSystemWidgetBundle("entity_admin_widgets");
  442 + this.deleteSystemWidgetBundle("edge_widgets");
442 443 installScripts.loadSystemWidgets();
443 444 }
444 445
... ...
... ... @@ -1157,4 +1157,26 @@ export class EntityService {
1157 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 35 import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component';
36 36 import { GatewayFormComponent } from './lib/gateway/gateway-form.component';
37 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 40 @NgModule({
40 41 declarations:
... ... @@ -45,6 +46,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor
45 46 AlarmsTableWidgetComponent,
46 47 TimeseriesTableWidgetComponent,
47 48 EntitiesHierarchyWidgetComponent,
  49 + EdgesOverviewWidgetComponent,
48 50 DateRangeNavigatorWidgetComponent,
49 51 DateRangeNavigatorPanelComponent,
50 52 MultipleInputWidgetComponent,
... ... @@ -63,6 +65,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor
63 65 AlarmsTableWidgetComponent,
64 66 TimeseriesTableWidgetComponent,
65 67 EntitiesHierarchyWidgetComponent,
  68 + EdgesOverviewWidgetComponent,
66 69 RpcWidgetsModule,
67 70 DateRangeNavigatorWidgetComponent,
68 71 MultipleInputWidgetComponent,
... ...
... ... @@ -33,11 +33,12 @@ import { WidgetsBundleComponent } from '@modules/home/pages/widget/widgets-bundl
33 33 import { NULL_UUID } from '@shared/models/id/has-uuid';
34 34 import { Store } from '@ngrx/store';
35 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 37 import { Authority } from '@shared/models/authority.enum';
38 38 import { DialogService } from '@core/services/dialog.service';
39 39 import { ImportExportService } from '@home/components/import-export/import-export.service';
40 40 import { Direction } from "@shared/models/page/sort-order";
  41 +import { map } from "rxjs/operators";
41 42
42 43 @Injectable()
43 44 export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableConfig<WidgetsBundle>> {
... ... @@ -106,7 +107,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
106 107 this.config.deleteEntitiesTitle = count => this.translate.instant('widgets-bundle.delete-widgets-bundles-title', {count});
107 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 111 this.config.loadEntity = id => this.widgetsService.getWidgetsBundle(id.id);
111 112 this.config.saveEntity = widgetsBundle => this.widgetsService.saveWidgetsBundle(widgetsBundle);
112 113 this.config.deleteEntity = id => this.widgetsService.deleteWidgetsBundle(id.id);
... ... @@ -119,6 +120,17 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
119 120 this.config.deleteEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority);
120 121 this.config.entitySelectionEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority);
121 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 134 return this.config;
123 135 }
124 136
... ...
... ... @@ -17,7 +17,7 @@
17 17 import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
18 18 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
19 19 import { Observable } from 'rxjs';
20   -import { share, tap } from 'rxjs/operators';
  20 +import { map, share, tap } from 'rxjs/operators';
21 21 import { Store } from '@ngrx/store';
22 22 import { AppState } from '@app/core/core.state';
23 23 import { coerceBooleanProperty } from '@angular/cdk/coercion';
... ... @@ -25,6 +25,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
25 25 import { WidgetService } from '@core/http/widget.service';
26 26 import { isDefined } from '@core/utils';
27 27 import { NULL_UUID } from '@shared/models/id/has-uuid';
  28 +import { getCurrentAuthState } from "@core/auth/auth.selectors";
28 29
29 30 @Component({
30 31 selector: 'tb-widgets-bundle-select',
... ... @@ -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 105 share()
98 106 );
99 107 }
... ...
... ... @@ -1233,7 +1233,7 @@
1233 1233 "assign-to-customer-text": "Please select the customer to assign the edge(s)",
1234 1234 "assign-edge-to-customer": "Assign Edge(s) To Customer",
1235 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 1237 "unassign-from-customer": "Unassign from customer",
1238 1238 "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer",
1239 1239 "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?",
... ... @@ -1287,7 +1287,8 @@
1287 1287 "no-downlinks-prompt": "No downlinks found",
1288 1288 "sync-process-started-successfully": "Sync process started successfully!",
1289 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 1293 "edge-event": {
1293 1294 "type-dashboard": "Dashboard",
... ...