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 @@ + +
+
+ {{ customerTitle }} + edge.widget-datasource-error +
+ +
+
+
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",