diff --git a/application/src/main/data/json/system/widget_bundles/edge_widgets.json b/application/src/main/data/json/system/widget_bundles/edge_widgets.json
new file mode 100644
index 0000000000..32047f20a4
--- /dev/null
+++ b/application/src/main/data/json/system/widget_bundles/edge_widgets.json
@@ -0,0 +1,25 @@
+{
+ "widgetsBundle": {
+ "alias": "edge_widgets",
+ "title": "Edge widgets",
+ "image": null
+ },
+ "widgetTypes": [
+ {
+ "alias": "edges_overview",
+ "name": "Edges Quick Overview",
+ "descriptor": {
+ "type": "latest",
+ "sizeX": 7.5,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n};\n\nself.onDestroy = function() {\n};\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}\n",
+ "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\":{}}"
+ }
+ }
+ ]
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
index fe1b06399c..70acadd3e8 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
this.deleteSystemWidgetBundle("input_widgets");
this.deleteSystemWidgetBundle("date");
this.deleteSystemWidgetBundle("entity_admin_widgets");
+ this.deleteSystemWidgetBundle("edge_widgets");
installScripts.loadSystemWidgets();
}
diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts
index 60d47cc4a5..1e152a4249 100644
--- a/ui-ngx/src/app/core/http/entity.service.ts
+++ b/ui-ngx/src/app/core/http/entity.service.ts
@@ -1157,4 +1157,26 @@ export class EntityService {
datasource.dataKeys.push(dataKey);
});
}
+
+ public getAssignedToEdgeEntitiesByType(edgeId: string, entityType: EntityType, pageLink: PageLink): Observable> {
+ let entitiesObservable: Observable>;
+ switch (entityType) {
+ case (EntityType.ASSET):
+ entitiesObservable = this.assetService.getEdgeAssets(edgeId, pageLink);
+ break;
+ case (EntityType.DEVICE):
+ entitiesObservable = this.deviceService.getEdgeDevices(edgeId, pageLink);
+ break;
+ case (EntityType.ENTITY_VIEW):
+ entitiesObservable = this.entityViewService.getEdgeEntityViews(edgeId, pageLink);
+ break;
+ case (EntityType.DASHBOARD):
+ entitiesObservable = this.dashboardService.getEdgeDashboards(edgeId, pageLink);
+ break;
+ case (EntityType.RULE_CHAIN):
+ entitiesObservable = this.ruleChainService.getEdgeRuleChains(edgeId, pageLink);
+ break;
+ }
+ return entitiesObservable;
+ }
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.html
new file mode 100644
index 0000000000..47cdf0e37f
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.html
@@ -0,0 +1,28 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss
new file mode 100644
index 0000000000..1f0bea102c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss
@@ -0,0 +1,128 @@
+/**
+ * Copyright © 2016-2020 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+:host-context(.tb-has-timewindow) {
+ .tb-edges-overview {
+ mat-toolbar {
+ height: 60px;
+ max-height: 60px;
+ .mat-toolbar-tools {
+ height: 60px;
+ max-height: 60px;
+ }
+ }
+ }
+}
+
+:host {
+ .tb-edges-overview {
+ mat-toolbar.mat-table-toolbar:not([color="primary"]) {
+ background: transparent;
+ }
+ mat-toolbar {
+ min-height: 39px;
+ max-height: 39px;
+ .mat-toolbar-tools {
+ min-height: 39px;
+ max-height: 39px;
+ }
+ }
+
+ .mat-subheader {
+ padding: 15px;
+ color: rgba(0,0,0,0.54);
+ font-size: 14px;
+ font-weight: 400;
+ }
+
+ .tb-entities-nav-tree-panel {
+ overflow-x: auto;
+ overflow-y: auto;
+ }
+ }
+}
+
+:host ::ng-deep {
+ .tb-nav-tree-container {
+ &.jstree-proton {
+ .jstree-anchor {
+ div.node-icon {
+ display: inline-block;
+ width: 22px;
+ height: 22px;
+ margin-right: 2px;
+ margin-bottom: 2px;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-attachment: scroll;
+ background-position: center center;
+ background-size: 18px 18px;
+ }
+
+ mat-icon.node-icon {
+ width: 22px;
+ min-width: 22px;
+ height: 22px;
+ min-height: 22px;
+ margin-right: 2px;
+ margin-bottom: 2px;
+ color: inherit;
+ vertical-align: middle;
+
+ &.material-icons {
+ font-size: 18px;
+ line-height: 22px;
+ text-align: center;
+ }
+ }
+
+ &.jstree-hovered:not(.jstree-clicked),
+ &.jstree-disabled {
+ div.node-icon {
+ opacity: .5;
+ }
+ }
+ }
+ }
+ }
+
+ @media (max-width: 768px) {
+ .tb-nav-tree-container {
+ &.jstree-proton-responsive {
+ .jstree-anchor {
+ div.node-icon {
+ width: 40px;
+ height: 40px;
+ margin: 0;
+ background-size: 24px 24px;
+ }
+
+ mat-icon.node-icon {
+ width: 40px;
+ min-width: 40px;
+ height: 40px;
+ min-height: 40px;
+ margin: 0;
+
+ &.material-icons {
+ font-size: 24px;
+ line-height: 40px;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.ts
new file mode 100644
index 0000000000..f8011bfb58
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.ts
@@ -0,0 +1,190 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { PageComponent } from '@shared/components/page.component';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { WidgetContext } from '@home/models/widget-component.models';
+import { Datasource, DatasourceType, WidgetConfig } from '@shared/models/widget.models';
+import { IWidgetSubscription } from '@core/api/widget-api.models';
+import { UtilsService } from '@core/services/utils.service';
+import { LoadNodesCallback } from '@shared/components/nav-tree.component';
+import { EntityType } from '@shared/models/entity-type.models';
+import {
+ EdgeGroupNodeData,
+ edgeGroupsNodeText,
+ edgeGroupsTypes,
+ entityNodeText,
+ EdgeOverviewNode,
+ EntityNodeData,
+ EntityNodeDatasource
+} from '@home/components/widget/lib/edges-overview-widget.models';
+import { EdgeService } from "@core/http/edge.service";
+import { EntityService } from "@core/http/entity.service";
+import { TranslateService } from "@ngx-translate/core";
+import { PageLink } from "@shared/models/page/page-link";
+import { BaseData, HasId } from "@shared/models/base-data";
+import { EntityId } from "@shared/models/id/entity-id";
+import { getCurrentAuthUser } from "@core/auth/auth.selectors";
+import { Authority } from "@shared/models/authority.enum";
+
+@Component({
+ selector: 'tb-edges-overview-widget',
+ templateUrl: './edges-overview-widget.component.html',
+ styleUrls: ['./edges-overview-widget.component.scss']
+})
+export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit {
+
+ @Input()
+ ctx: WidgetContext;
+
+ public toastTargetId = 'edges-overview-' + this.utils.guid();
+ public customerTitle: string = null;
+ public edgeIsDatasource: boolean = true;
+
+ private widgetConfig: WidgetConfig;
+ private subscription: IWidgetSubscription;
+ private datasources: Array;
+
+ private nodeIdCounter = 0;
+
+ private entityNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {};
+ private entityGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {};
+
+ constructor(protected store: Store,
+ private edgeService: EdgeService,
+ private entityService: EntityService,
+ private translateService: TranslateService,
+ private utils: UtilsService,
+ private cd: ChangeDetectorRef) {
+ super(store);
+ }
+
+ ngOnInit(): void {
+ this.widgetConfig = this.ctx.widgetConfig;
+ this.subscription = this.ctx.defaultSubscription;
+ this.datasources = this.subscription.datasources as Array;
+ this.ctx.updateWidgetParams();
+ }
+
+ public loadNodes: LoadNodesCallback = (node, cb) => {
+ const datasource: Datasource = this.datasources[0];
+ if (node.id === '#' && datasource) {
+ if (datasource.type === DatasourceType.entity && datasource.entity.id.entityType === EntityType.EDGE) {
+ var selectedEdge: BaseData = datasource.entity;
+ this.getCustomerTitle(selectedEdge.id.id);
+ this.ctx.widgetTitle = selectedEdge.name;
+ cb(this.loadNodesForEdge(selectedEdge.id.id, selectedEdge));
+ } else if (datasource.type === DatasourceType.function) {
+ cb(this.loadNodesForEdge(datasource.entityId, datasource.entity));
+ } else {
+ this.edgeIsDatasource = false;
+ cb([]);
+ }
+ }
+ else if (node.data && node.data.entity.id.entityType === EntityType.EDGE) {
+ const edgeId = node.data.entity.id.id;
+ const entityType = node.data.entityType;
+ const pageLink = new PageLink(datasource.pageLink.pageSize);
+ this.entityService.getAssignedToEdgeEntitiesByType(edgeId, entityType, pageLink).subscribe(
+ (entities) => {
+ if (entities.data.length > 0) {
+ cb(this.entitiesToNodes(node.id, entities.data));
+ } else {
+ cb([]);
+ }
+ }
+ )
+ } else {
+ cb([]);
+ }
+ }
+
+ private loadNodesForEdge(parentNodeId: string, entity: BaseData): EdgeOverviewNode[] {
+ const nodes: EdgeOverviewNode[] = [];
+ const nodesMap = {};
+ this.entityGroupsNodesMap[parentNodeId] = nodesMap;
+ const authUser = getCurrentAuthUser(this.store);
+ var allowedGroupTypes: EntityType[] = edgeGroupsTypes;
+ if (authUser.authority === Authority.CUSTOMER_USER) {
+ allowedGroupTypes = edgeGroupsTypes.filter(type => type !== EntityType.RULE_CHAIN);
+ }
+ allowedGroupTypes.forEach((entityType) => {
+ const node: EdgeOverviewNode = {
+ id: (++this.nodeIdCounter)+'',
+ icon: false,
+ text: edgeGroupsNodeText(this.translateService, entityType),
+ children: true,
+ data: {
+ entityType,
+ entity,
+ internalId: entity.id.id + '_' + entityType
+ } as EdgeGroupNodeData
+ };
+ nodes.push(node);
+ nodesMap[entityType] = node.id;
+ });
+ return nodes;
+ }
+
+ private createEntityNode(parentNodeId: string, entity: BaseData): EdgeOverviewNode {
+ let nodesMap = this.entityNodesMap[parentNodeId];
+ if (!nodesMap) {
+ nodesMap = {};
+ this.entityNodesMap[parentNodeId] = nodesMap;
+ }
+ const node: EdgeOverviewNode = {
+ id: (++this.nodeIdCounter)+'',
+ icon: false,
+ text: entityNodeText(entity),
+ children: false,
+ state: {
+ disabled: false
+ },
+ data: {
+ entity: entity,
+ internalId: entity.id.id
+ } as EntityNodeData
+ };
+ nodesMap[entity.id.id] = node.id;
+ return node;
+ }
+
+ private entitiesToNodes(parentNodeId: string, entities: BaseData[]): EdgeOverviewNode[] {
+ const nodes: EdgeOverviewNode[] = [];
+ this.entityNodesMap[parentNodeId] = {};
+ if (entities) {
+ entities.forEach((entity) => {
+ const node = this.createEntityNode(parentNodeId, entity);
+ nodes.push(node);
+ });
+ }
+ return nodes;
+ }
+
+ private getCustomerTitle(edgeId: string) {
+ this.edgeService.getEdgeInfo(edgeId).subscribe(
+ (edge) => {
+ if (edge.customerTitle) {
+ this.customerTitle = this.translateService.instant('edge.assigned-to-customer', {customerTitle: edge.customerTitle});
+ } else {
+ this.customerTitle = null;
+ }
+ this.cd.detectChanges();
+ });
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.models.ts
new file mode 100644
index 0000000000..ca561b8693
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.models.ts
@@ -0,0 +1,108 @@
+///
+/// Copyright © 2016-2020 The Thingsboard Authors
+///
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+///
+
+import { NavTreeNode } from '@shared/components/nav-tree.component';
+import { Datasource } from '@shared/models/widget.models';
+import { EntityType } from '@shared/models/entity-type.models';
+import { TranslateService } from "@ngx-translate/core";
+import { BaseData, HasId } from "@shared/models/base-data";
+
+export interface EntityNodeDatasource extends Datasource {
+ nodeId: string;
+}
+
+export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string {
+ const nodeIcon = materialIconByEntityType(entityType);
+ const nodeText = textForEdgeGroupsType(translate, entityType);
+ return nodeIcon + nodeText;
+}
+
+export function entityNodeText(entity: any): string {
+ const nodeIcon = materialIconByEntityType(entity.id.entityType);
+ const nodeText = entity.name;
+ return nodeIcon + nodeText;
+}
+
+export function materialIconByEntityType(entityType: EntityType): string {
+ let materialIcon = 'insert_drive_file';
+ switch (entityType) {
+ case EntityType.DEVICE:
+ materialIcon = 'devices_other';
+ break;
+ case EntityType.ASSET:
+ materialIcon = 'domain';
+ break;
+ case EntityType.DASHBOARD:
+ materialIcon = 'dashboards';
+ break;
+ case EntityType.ENTITY_VIEW:
+ materialIcon = 'view_quilt';
+ break;
+ case EntityType.RULE_CHAIN:
+ materialIcon = 'settings_ethernet';
+ break;
+ }
+ return '' + materialIcon + '';
+}
+
+export function textForEdgeGroupsType(translate: TranslateService, entityType: EntityType): string {
+ let textForEdgeGroupsType: string = '';
+ switch (entityType) {
+ case EntityType.DEVICE:
+ textForEdgeGroupsType = 'device.devices';
+ break;
+ case EntityType.ASSET:
+ textForEdgeGroupsType = 'asset.assets';
+ break;
+ case EntityType.DASHBOARD:
+ textForEdgeGroupsType = 'dashboard.dashboards';
+ break;
+ case EntityType.ENTITY_VIEW:
+ textForEdgeGroupsType = 'entity-view.entity-views';
+ break;
+ case EntityType.RULE_CHAIN:
+ textForEdgeGroupsType = 'rulechain.rulechains';
+ break;
+ }
+ return translate.instant(textForEdgeGroupsType);
+}
+
+export const edgeGroupsTypes: EntityType[] = [
+ EntityType.ASSET,
+ EntityType.DEVICE,
+ EntityType.ENTITY_VIEW,
+ EntityType.DASHBOARD,
+ EntityType.RULE_CHAIN
+]
+
+export interface EdgeOverviewNode extends NavTreeNode {
+ data?: EdgeOverviewNodeData;
+}
+
+export type EdgeOverviewNodeData = EdgeGroupNodeData | EntityNodeData;
+
+export interface EdgeGroupNodeData extends BaseEdgeOverviewNodeData {
+ entityType: EntityType;
+ entity: BaseData;
+}
+
+export interface EntityNodeData extends BaseEdgeOverviewNodeData {
+ entity: BaseData;
+}
+
+export interface BaseEdgeOverviewNodeData {
+ internalId: string;
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
index c2a158fcdf..f2390d099a 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
@@ -35,6 +35,7 @@ import { TripAnimationComponent } from './trip-animation/trip-animation.componen
import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component';
import { GatewayFormComponent } from './lib/gateway/gateway-form.component';
import { ImportExportService } from '@home/components/import-export/import-export.service';
+import { EdgesOverviewWidgetComponent } from "@home/components/widget/lib/edges-overview-widget.component";
@NgModule({
declarations:
@@ -45,6 +46,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor
AlarmsTableWidgetComponent,
TimeseriesTableWidgetComponent,
EntitiesHierarchyWidgetComponent,
+ EdgesOverviewWidgetComponent,
DateRangeNavigatorWidgetComponent,
DateRangeNavigatorPanelComponent,
MultipleInputWidgetComponent,
@@ -63,6 +65,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor
AlarmsTableWidgetComponent,
TimeseriesTableWidgetComponent,
EntitiesHierarchyWidgetComponent,
+ EdgesOverviewWidgetComponent,
RpcWidgetsModule,
DateRangeNavigatorWidgetComponent,
MultipleInputWidgetComponent,
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts
index c59dd6cc63..13a5ff9da1 100644
--- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts
@@ -33,11 +33,12 @@ import { WidgetsBundleComponent } from '@modules/home/pages/widget/widgets-bundl
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
-import { getCurrentAuthUser } from '@app/core/auth/auth.selectors';
+import { getCurrentAuthState, getCurrentAuthUser } from '@app/core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum';
import { DialogService } from '@core/services/dialog.service';
import { ImportExportService } from '@home/components/import-export/import-export.service';
import { Direction } from "@shared/models/page/sort-order";
+import { map } from "rxjs/operators";
@Injectable()
export class WidgetsBundlesTableConfigResolver implements Resolve> {
@@ -106,7 +107,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve this.translate.instant('widgets-bundle.delete-widgets-bundles-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('widgets-bundle.delete-widgets-bundles-text');
- this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink);
+
this.config.loadEntity = id => this.widgetsService.getWidgetsBundle(id.id);
this.config.saveEntity = widgetsBundle => this.widgetsService.saveWidgetsBundle(widgetsBundle);
this.config.deleteEntity = id => this.widgetsService.deleteWidgetsBundle(id.id);
@@ -119,6 +120,17 @@ export class WidgetsBundlesTableConfigResolver implements Resolve this.isWidgetsBundleEditable(widgetsBundle, authUser.authority);
this.config.entitySelectionEnabled = (widgetsBundle) => this.isWidgetsBundleEditable(widgetsBundle, authUser.authority);
this.config.detailsReadonly = (widgetsBundle) => !this.isWidgetsBundleEditable(widgetsBundle, authUser.authority);
+ const authState = getCurrentAuthState(this.store);
+ if (!authState.edgesSupportEnabled) {
+ this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink).pipe(
+ map((widgetBundles) => {
+ widgetBundles.data = widgetBundles.data.filter(widgetBundle => widgetBundle.alias !== 'edge_widgets');
+ return widgetBundles;
+ })
+ );
+ } else {
+ this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetBundles(pageLink);
+ }
return this.config;
}
diff --git a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts
index 90952c1ddb..97542d8012 100644
--- a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts
+++ b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts
@@ -17,7 +17,7 @@
import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
-import { share, tap } from 'rxjs/operators';
+import { map, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@@ -25,6 +25,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
import { WidgetService } from '@core/http/widget.service';
import { isDefined } from '@core/utils';
import { NULL_UUID } from '@shared/models/id/has-uuid';
+import { getCurrentAuthState } from "@core/auth/auth.selectors";
@Component({
selector: 'tb-widgets-bundle-select',
@@ -94,6 +95,13 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni
}
}
}),
+ map((widgetsBundles) => {
+ const authState = getCurrentAuthState(this.store);
+ if (!authState.edgesSupportEnabled) {
+ widgetsBundles = widgetsBundles.filter(widgetsBundle => widgetsBundle.alias !== 'edge_widgets');
+ }
+ return widgetsBundles;
+ }),
share()
);
}
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index bf7b38735f..42a0a9057d 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -1233,7 +1233,7 @@
"assign-to-customer-text": "Please select the customer to assign the edge(s)",
"assign-edge-to-customer": "Assign Edge(s) To Customer",
"assign-edge-to-customer-text": "Please select the edges to assign to the customer",
- "assigned-to-customer": "Assigned to customer",
+ "assigned-to-customer": "Assigned to: {{customerTitle}}",
"unassign-from-customer": "Unassign from customer",
"assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer",
"unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?",
@@ -1287,7 +1287,8 @@
"no-downlinks-prompt": "No downlinks found",
"sync-process-started-successfully": "Sync process started successfully!",
"missing-related-rule-chains-title": "Edge has missing related rule chain(s)",
- "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge.
List of missing rule chain(s):
{{missingRuleChains}}"
+ "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge.
List of missing rule chain(s):
{{missingRuleChains}}",
+ "widget-datasource-error": "This widget supports only EDGE entity datasource"
},
"edge-event": {
"type-dashboard": "Dashboard",