Commit 846f5ba86b5d3cc5ff83df8d7a7fd064589330a2

Authored by Volodymyr Babak
2 parents 64694532 5a04ebfd

Merge remote-tracking branch 'deaflynx/develop/3.3-edge' into develop/3.3-edge

Showing 39 changed files with 353 additions and 78 deletions
@@ -492,6 +492,7 @@ public class AssetController extends BaseController { @@ -492,6 +492,7 @@ public class AssetController extends BaseController {
492 @PathVariable(EDGE_ID) String strEdgeId, 492 @PathVariable(EDGE_ID) String strEdgeId,
493 @RequestParam int pageSize, 493 @RequestParam int pageSize,
494 @RequestParam int page, 494 @RequestParam int page,
  495 + @RequestParam(required = false) String type,
495 @RequestParam(required = false) String textSearch, 496 @RequestParam(required = false) String textSearch,
496 @RequestParam(required = false) String sortProperty, 497 @RequestParam(required = false) String sortProperty,
497 @RequestParam(required = false) String sortOrder, 498 @RequestParam(required = false) String sortOrder,
@@ -503,7 +504,11 @@ public class AssetController extends BaseController { @@ -503,7 +504,11 @@ public class AssetController extends BaseController {
503 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 504 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
504 checkEdgeId(edgeId, Operation.READ); 505 checkEdgeId(edgeId, Operation.READ);
505 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); 506 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
506 - return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); 507 + if (type != null && type.trim().length() > 0) {
  508 + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
  509 + } else {
  510 + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
  511 + }
507 } catch (Exception e) { 512 } catch (Exception e) {
508 throw handleException(e); 513 throw handleException(e);
509 } 514 }
@@ -677,6 +677,7 @@ public class EntityViewController extends BaseController { @@ -677,6 +677,7 @@ public class EntityViewController extends BaseController {
677 @PathVariable(EDGE_ID) String strEdgeId, 677 @PathVariable(EDGE_ID) String strEdgeId,
678 @RequestParam int pageSize, 678 @RequestParam int pageSize,
679 @RequestParam int page, 679 @RequestParam int page,
  680 + @RequestParam(required = false) String type,
680 @RequestParam(required = false) String textSearch, 681 @RequestParam(required = false) String textSearch,
681 @RequestParam(required = false) String sortProperty, 682 @RequestParam(required = false) String sortProperty,
682 @RequestParam(required = false) String sortOrder, 683 @RequestParam(required = false) String sortOrder,
@@ -688,7 +689,11 @@ public class EntityViewController extends BaseController { @@ -688,7 +689,11 @@ public class EntityViewController extends BaseController {
688 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 689 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
689 checkEdgeId(edgeId, Operation.READ); 690 checkEdgeId(edgeId, Operation.READ);
690 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); 691 TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
691 - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); 692 + if (type != null && type.trim().length() > 0) {
  693 + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
  694 + } else {
  695 + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
  696 + }
692 } catch (Exception e) { 697 } catch (Exception e) {
693 throw handleException(e); 698 throw handleException(e);
694 } 699 }
@@ -82,4 +82,6 @@ public interface AssetService { @@ -82,4 +82,6 @@ public interface AssetService {
82 Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId); 82 Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
83 83
84 PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); 84 PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
  85 +
  86 + PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TimePageLink pageLink);
85 } 87 }
@@ -83,4 +83,6 @@ public interface EntityViewService { @@ -83,4 +83,6 @@ public interface EntityViewService {
83 EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId); 83 EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
84 84
85 PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); 85 PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
  86 +
  87 + PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TimePageLink pageLink);
86 } 88 }
@@ -177,4 +177,15 @@ public interface AssetDao extends Dao<Asset>, TenantEntityDao { @@ -177,4 +177,15 @@ public interface AssetDao extends Dao<Asset>, TenantEntityDao {
177 * @return the list of asset objects 177 * @return the list of asset objects
178 */ 178 */
179 PageData<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); 179 PageData<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
  180 +
  181 + /**
  182 + * Find assets by tenantId, edgeId, type and page link.
  183 + *
  184 + * @param tenantId the tenantId
  185 + * @param edgeId the edgeId
  186 + * @param type the type
  187 + * @param pageLink the page link
  188 + * @return the list of asset objects
  189 + */
  190 + PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TimePageLink pageLink);
180 } 191 }
@@ -372,6 +372,16 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -372,6 +372,16 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
372 return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); 372 return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
373 } 373 }
374 374
  375 + @Override
  376 + public PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TimePageLink pageLink) {
  377 + log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink);
  378 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  379 + validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
  380 + validateString(type, "Incorrect type " + type);
  381 + validatePageLink(pageLink);
  382 + return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
  383 + }
  384 +
375 private DataValidator<Asset> assetValidator = 385 private DataValidator<Asset> assetValidator =
376 new DataValidator<Asset>() { 386 new DataValidator<Asset>() {
377 387
@@ -165,4 +165,18 @@ public interface EntityViewDao extends Dao<EntityView> { @@ -165,4 +165,18 @@ public interface EntityViewDao extends Dao<EntityView> {
165 UUID edgeId, 165 UUID edgeId,
166 PageLink pageLink); 166 PageLink pageLink);
167 167
  168 + /**
  169 + * Find entity views by tenantId, edgeId, type and page link.
  170 + *
  171 + * @param tenantId the tenantId
  172 + * @param edgeId the edgeId
  173 + * @param type the type
  174 + * @param pageLink the page link
  175 + * @return the list of entity view objects
  176 + */
  177 + PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId,
  178 + UUID edgeId,
  179 + String type,
  180 + PageLink pageLink);
  181 +
168 } 182 }
@@ -390,6 +390,16 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @@ -390,6 +390,16 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
390 return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); 390 return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
391 } 391 }
392 392
  393 + @Override
  394 + public PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TimePageLink pageLink) {
  395 + log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
  396 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  397 + validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
  398 + validateString(type, "Incorrect type " + type);
  399 + validatePageLink(pageLink);
  400 + return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
  401 + }
  402 +
393 private DataValidator<EntityView> entityViewValidator = 403 private DataValidator<EntityView> entityViewValidator =
394 new DataValidator<EntityView>() { 404 new DataValidator<EntityView>() {
395 405
@@ -176,4 +176,4 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T @@ -176,4 +176,4 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
176 edge.setAdditionalInfo(additionalInfo); 176 edge.setAdditionalInfo(additionalInfo);
177 return edge; 177 return edge;
178 } 178 }
179 -}  
  179 +}
@@ -132,5 +132,16 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, @@ -132,5 +132,16 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity,
132 @Param("searchText") String searchText, 132 @Param("searchText") String searchText,
133 Pageable pageable); 133 Pageable pageable);
134 134
  135 + @Query("SELECT a FROM AssetEntity a, RelationEntity re WHERE a.tenantId = :tenantId " +
  136 + "AND a.id = re.toId AND re.toType = 'ASSET' AND re.relationTypeGroup = 'EDGE' " +
  137 + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " +
  138 + "AND a.type = :type " +
  139 + "AND LOWER(a.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
  140 + Page<AssetEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") UUID tenantId,
  141 + @Param("edgeId") UUID edgeId,
  142 + @Param("type") String type,
  143 + @Param("searchText") String searchText,
  144 + Pageable pageable);
  145 +
135 Long countByTenantIdAndTypeIsNot(UUID tenantId, String type); 146 Long countByTenantIdAndTypeIsNot(UUID tenantId, String type);
136 } 147 }
@@ -200,6 +200,18 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im @@ -200,6 +200,18 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
200 } 200 }
201 201
202 @Override 202 @Override
  203 + public PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TimePageLink pageLink) {
  204 + log.debug("Try to find assets by tenantId [{}], edgeId [{}], type [{}] and pageLink [{}]", tenantId, edgeId, type, pageLink);
  205 + return DaoUtil.toPageData(assetRepository
  206 + .findByTenantIdAndEdgeIdAndType(
  207 + tenantId,
  208 + edgeId,
  209 + type,
  210 + Objects.toString(pageLink.getTextSearch(), ""),
  211 + DaoUtil.toPageable(pageLink)));
  212 + }
  213 +
  214 + @Override
203 public Long countByTenantId(TenantId tenantId) { 215 public Long countByTenantId(TenantId tenantId) {
204 return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE); 216 return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE);
205 } 217 }
@@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable; @@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable;
20 import org.springframework.data.jpa.repository.Query; 20 import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 -import org.thingsboard.server.dao.model.sql.AssetEntity;  
24 import org.thingsboard.server.dao.model.sql.DeviceEntity; 23 import org.thingsboard.server.dao.model.sql.DeviceEntity;
25 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity; 24 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
26 25
@@ -129,4 +129,15 @@ public interface EntityViewRepository extends PagingAndSortingRepository<EntityV @@ -129,4 +129,15 @@ public interface EntityViewRepository extends PagingAndSortingRepository<EntityV
129 @Param("edgeId") UUID edgeId, 129 @Param("edgeId") UUID edgeId,
130 @Param("searchText") String searchText, 130 @Param("searchText") String searchText,
131 Pageable pageable); 131 Pageable pageable);
  132 +
  133 + @Query("SELECT ev FROM EntityViewEntity ev, RelationEntity re WHERE ev.tenantId = :tenantId " +
  134 + "AND ev.id = re.toId AND re.toType = 'ENTITY_VIEW' AND re.relationTypeGroup = 'EDGE' " +
  135 + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " +
  136 + "AND ev.type = :type " +
  137 + "AND LOWER(ev.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
  138 + Page<EntityViewEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") UUID tenantId,
  139 + @Param("edgeId") UUID edgeId,
  140 + @Param("type") String type,
  141 + @Param("searchText") String searchText,
  142 + Pageable pageable);
132 } 143 }
@@ -192,4 +192,16 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, @@ -192,4 +192,16 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity,
192 Objects.toString(pageLink.getTextSearch(), ""), 192 Objects.toString(pageLink.getTextSearch(), ""),
193 DaoUtil.toPageable(pageLink))); 193 DaoUtil.toPageable(pageLink)));
194 } 194 }
  195 +
  196 + @Override
  197 + public PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) {
  198 + log.debug("Try to find entity views by tenantId [{}], edgeId [{}], type [{}] and pageLink [{}]", tenantId, edgeId, type, pageLink);
  199 + return DaoUtil.toPageData(entityViewRepository
  200 + .findByTenantIdAndEdgeIdAndType(
  201 + tenantId,
  202 + edgeId,
  203 + type,
  204 + Objects.toString(pageLink.getTextSearch(), ""),
  205 + DaoUtil.toPageable(pageLink)));
  206 + }
195 } 207 }
@@ -99,7 +99,7 @@ export class AssetService { @@ -99,7 +99,7 @@ export class AssetService {
99 return this.http.delete(`/api/edge/${edgeId}/asset/${assetId}`, defaultHttpOptionsFromConfig(config)); 99 return this.http.delete(`/api/edge/${edgeId}/asset/${assetId}`, defaultHttpOptionsFromConfig(config));
100 } 100 }
101 101
102 - public getEdgeAssets(edgeId, pageLink: PageLink, type: string = '', 102 + public getEdgeAssets(edgeId: string, pageLink: PageLink, type: string = '',
103 config?: RequestConfig): Observable<PageData<AssetInfo>> { 103 config?: RequestConfig): Observable<PageData<AssetInfo>> {
104 return this.http.get<PageData<AssetInfo>>(`/api/edge/${edgeId}/assets${pageLink.toQuery()}&type=${type}`, 104 return this.http.get<PageData<AssetInfo>>(`/api/edge/${edgeId}/assets${pageLink.toQuery()}&type=${type}`,
105 defaultHttpOptionsFromConfig(config)); 105 defaultHttpOptionsFromConfig(config));
@@ -76,12 +76,6 @@ export class EdgeService { @@ -76,12 +76,6 @@ export class EdgeService {
76 defaultHttpOptionsFromConfig(config)); 76 defaultHttpOptionsFromConfig(config));
77 } 77 }
78 78
79 - public setRootRuleChain(edgeId: string, ruleChainId: string,  
80 - config?: RequestConfig): Observable<Edge> {  
81 - return this.http.post<Edge>(`/api/edge/${edgeId}/${ruleChainId}/root`,  
82 - defaultHttpOptionsFromConfig(config));  
83 - }  
84 -  
85 public getTenantEdgeInfos(pageLink: PageLink, type: string = '', 79 public getTenantEdgeInfos(pageLink: PageLink, type: string = '',
86 config?: RequestConfig): Observable<PageData<EdgeInfo>> { 80 config?: RequestConfig): Observable<PageData<EdgeInfo>> {
87 return this.http.get<PageData<EdgeInfo>>(`/api/tenant/edgeInfos${pageLink.toQuery()}&type=${type}`, 81 return this.http.get<PageData<EdgeInfo>>(`/api/tenant/edgeInfos${pageLink.toQuery()}&type=${type}`,
@@ -44,6 +44,7 @@ import { TranslateService } from '@ngx-translate/core'; @@ -44,6 +44,7 @@ import { TranslateService } from '@ngx-translate/core';
44 import { EntityType } from '@shared/models/entity-type.models'; 44 import { EntityType } from '@shared/models/entity-type.models';
45 import { deepClone, snakeCase } from '@core/utils'; 45 import { deepClone, snakeCase } from '@core/utils';
46 import { DebugRuleNodeEventBody } from '@app/shared/models/event.models'; 46 import { DebugRuleNodeEventBody } from '@app/shared/models/event.models';
  47 +import { Edge } from "@shared/models/edge.models";
47 48
48 @Injectable({ 49 @Injectable({
49 providedIn: 'root' 50 providedIn: 'root'
@@ -323,4 +324,9 @@ export class RuleChainService { @@ -323,4 +324,9 @@ export class RuleChainService {
323 return this.http.get<Array<RuleChain>>(`/api/ruleChain/defaultEdgeRuleChains`, defaultHttpOptionsFromConfig(config)); 324 return this.http.get<Array<RuleChain>>(`/api/ruleChain/defaultEdgeRuleChains`, defaultHttpOptionsFromConfig(config));
324 } 325 }
325 326
  327 + public setEdgeRootRuleChain(edgeId: string, ruleChainId: string, config?: RequestConfig): Observable<Edge> { //TODO deaflynx EdgeInfo vs. Edge check usage
  328 + return this.http.post<Edge>(`/api/edge/${edgeId}/${ruleChainId}/root`,
  329 + defaultHttpOptionsFromConfig(config));
  330 + }
  331 +
326 } 332 }
@@ -101,6 +101,17 @@ export class EntityFilterViewComponent implements ControlValueAccessor { @@ -101,6 +101,17 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
101 {deviceType}); 101 {deviceType});
102 } 102 }
103 break; 103 break;
  104 + case AliasFilterType.edgeType:
  105 + const edgeType = this.filter.edgeType;
  106 + prefix = this.filter.edgeNameFilter;
  107 + if (prefix && prefix.length) {
  108 + this.filterDisplayValue = this.translate.instant('alias.filter-type-edge-type-and-name-description',
  109 + {edgeType, prefix});
  110 + } else {
  111 + this.filterDisplayValue = this.translate.instant('alias.filter-type-edge-type-description',
  112 + {edgeType});
  113 + }
  114 + break;
104 case AliasFilterType.entityViewType: 115 case AliasFilterType.entityViewType:
105 const entityView = this.filter.entityViewType; 116 const entityView = this.filter.entityViewType;
106 prefix = this.filter.entityViewNameFilter; 117 prefix = this.filter.entityViewNameFilter;
@@ -166,6 +177,7 @@ export class EntityFilterViewComponent implements ControlValueAccessor { @@ -166,6 +177,7 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
166 break; 177 break;
167 case AliasFilterType.assetSearchQuery: 178 case AliasFilterType.assetSearchQuery:
168 case AliasFilterType.deviceSearchQuery: 179 case AliasFilterType.deviceSearchQuery:
  180 + case AliasFilterType.edgeSearchQuery:
169 case AliasFilterType.entityViewSearchQuery: 181 case AliasFilterType.entityViewSearchQuery:
170 allEntitiesText = this.translate.instant('alias.all-entities'); 182 allEntitiesText = this.translate.instant('alias.all-entities');
171 anyRelationText = this.translate.instant('alias.any-relation'); 183 anyRelationText = this.translate.instant('alias.any-relation');
@@ -207,6 +219,16 @@ export class EntityFilterViewComponent implements ControlValueAccessor { @@ -207,6 +219,16 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
207 this.filterDisplayValue = this.translate.instant('alias.filter-type-device-search-query-description', 219 this.filterDisplayValue = this.translate.instant('alias.filter-type-device-search-query-description',
208 translationValues 220 translationValues
209 ); 221 );
  222 + } else if (this.filter.type === AliasFilterType.edgeSearchQuery) {
  223 + const edgeTypesQuoted = [];
  224 + this.filter.edgeTypes.forEach((filterEdgeType) => {
  225 + edgeTypesQuoted.push(`'${filterEdgeType}'`);
  226 + });
  227 + const edgeTypesText = edgeTypesQuoted.join(', ');
  228 + translationValues.edgeTypes = edgeTypesText;
  229 + this.filterDisplayValue = this.translate.instant('alias.filter-type-edge-search-query-description',
  230 + translationValues
  231 + );
210 } else if (this.filter.type === AliasFilterType.entityViewSearchQuery) { 232 } else if (this.filter.type === AliasFilterType.entityViewSearchQuery) {
211 const entityViewTypesQuoted = []; 233 const entityViewTypesQuoted = [];
212 this.filter.entityViewTypes.forEach((filterEntityViewType) => { 234 this.filter.entityViewTypes.forEach((filterEntityViewType) => {
@@ -103,6 +103,16 @@ @@ -103,6 +103,16 @@
103 <input matInput formControlName="entityViewNameFilter"> 103 <input matInput formControlName="entityViewNameFilter">
104 </mat-form-field> 104 </mat-form-field>
105 </ng-template> 105 </ng-template>
  106 + <ng-template [ngSwitchCase]="aliasFilterType.edgeType">
  107 + <tb-entity-subtype-autocomplete required
  108 + formControlName="edgeType"
  109 + [entityType]="entityType.EDGE">
  110 + </tb-entity-subtype-autocomplete>
  111 + <mat-form-field class="mat-block">
  112 + <mat-label translate>edge.name-starts-with</mat-label>
  113 + <input matInput formControlName="edgeNameFilter">
  114 + </mat-form-field>
  115 + </ng-template>
106 <ng-template [ngSwitchCase]="aliasFilterType.relationsQuery"> 116 <ng-template [ngSwitchCase]="aliasFilterType.relationsQuery">
107 <section fxLayout="column" id="relationsQueryFilter"> 117 <section fxLayout="column" id="relationsQueryFilter">
108 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label> 118 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
@@ -169,6 +179,7 @@ @@ -169,6 +179,7 @@
169 </ng-template> 179 </ng-template>
170 <ng-template [ngSwitchCase]="entityFilterFormGroup.get('type').value === aliasFilterType.assetSearchQuery || 180 <ng-template [ngSwitchCase]="entityFilterFormGroup.get('type').value === aliasFilterType.assetSearchQuery ||
171 entityFilterFormGroup.get('type').value === aliasFilterType.deviceSearchQuery || 181 entityFilterFormGroup.get('type').value === aliasFilterType.deviceSearchQuery ||
  182 + entityFilterFormGroup.get('type').value === aliasFilterType.edgeSearchQuery ||
172 entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ? 183 entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ?
173 entityFilterFormGroup.get('type').value : ''"> 184 entityFilterFormGroup.get('type').value : ''">
174 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label> 185 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
@@ -248,6 +259,14 @@ @@ -248,6 +259,14 @@
248 formControlName="deviceTypes"> 259 formControlName="deviceTypes">
249 </tb-entity-subtype-list> 260 </tb-entity-subtype-list>
250 </ng-template> 261 </ng-template>
  262 + <ng-template [ngSwitchCase]="aliasFilterType.edgeSearchQuery">
  263 + <div class="mat-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>edge.edge-types</div>
  264 + <tb-entity-subtype-list
  265 + required
  266 + [entityType]="entityType.EDGE"
  267 + formControlName="edgeTypes">
  268 + </tb-entity-subtype-list>
  269 + </ng-template>
251 <ng-template [ngSwitchCase]="aliasFilterType.entityViewSearchQuery"> 270 <ng-template [ngSwitchCase]="aliasFilterType.entityViewSearchQuery">
252 <div class="mat-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>entity-view.entity-view-types</div> 271 <div class="mat-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>entity-view.entity-view-types</div>
253 <tb-entity-subtype-list 272 <tb-entity-subtype-list
@@ -141,6 +141,12 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit { @@ -141,6 +141,12 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
141 deviceNameFilter: [filter ? filter.deviceNameFilter : '', []], 141 deviceNameFilter: [filter ? filter.deviceNameFilter : '', []],
142 }); 142 });
143 break; 143 break;
  144 + case AliasFilterType.edgeType:
  145 + this.filterFormGroup = this.fb.group({
  146 + edgeType: [filter ? filter.edgeType : null, [Validators.required]],
  147 + edgeNameFilter: [filter ? filter.edgeNameFilter : '', []],
  148 + });
  149 + break;
144 case AliasFilterType.entityViewType: 150 case AliasFilterType.entityViewType:
145 this.filterFormGroup = this.fb.group({ 151 this.filterFormGroup = this.fb.group({
146 entityViewType: [filter ? filter.entityViewType : null, [Validators.required]], 152 entityViewType: [filter ? filter.entityViewType : null, [Validators.required]],
@@ -153,6 +159,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit { @@ -153,6 +159,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
153 case AliasFilterType.relationsQuery: 159 case AliasFilterType.relationsQuery:
154 case AliasFilterType.assetSearchQuery: 160 case AliasFilterType.assetSearchQuery:
155 case AliasFilterType.deviceSearchQuery: 161 case AliasFilterType.deviceSearchQuery:
  162 + case AliasFilterType.edgeSearchQuery:
156 case AliasFilterType.entityViewSearchQuery: 163 case AliasFilterType.entityViewSearchQuery:
157 this.filterFormGroup = this.fb.group({ 164 this.filterFormGroup = this.fb.group({
158 rootStateEntity: [filter ? filter.rootStateEntity : false, []], 165 rootStateEntity: [filter ? filter.rootStateEntity : false, []],
@@ -179,6 +186,9 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit { @@ -179,6 +186,9 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
179 } else if (type === AliasFilterType.deviceSearchQuery) { 186 } else if (type === AliasFilterType.deviceSearchQuery) {
180 this.filterFormGroup.addControl('deviceTypes', 187 this.filterFormGroup.addControl('deviceTypes',
181 this.fb.control(filter ? filter.deviceTypes : [], [Validators.required])); 188 this.fb.control(filter ? filter.deviceTypes : [], [Validators.required]));
  189 + } else if (type === AliasFilterType.edgeSearchQuery) {
  190 + this.filterFormGroup.addControl('edgeTypes',
  191 + this.fb.control(filter ? filter.edgeTypes : [], [Validators.required]));
182 } else if (type === AliasFilterType.entityViewSearchQuery) { 192 } else if (type === AliasFilterType.entityViewSearchQuery) {
183 this.filterFormGroup.addControl('entityViewTypes', 193 this.filterFormGroup.addControl('entityViewTypes',
184 this.fb.control(filter ? filter.entityViewTypes : [], [Validators.required])); 194 this.fb.control(filter ? filter.entityViewTypes : [], [Validators.required]));
@@ -190,9 +190,9 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { @@ -190,9 +190,9 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
190 this.columns.push( 190 this.columns.push(
191 new EntityTableColumn<Event>('type', 'event.type', '100%', 191 new EntityTableColumn<Event>('type', 'event.type', '100%',
192 (entity) => entity.type, entity => ({}), false), 192 (entity) => entity.type, entity => ({}), false),
193 - new EntityTableColumn<Event>('action', 'event.action', '100%', 193 + new EntityTableColumn<Event>('action', 'edge.event-action', '100%',
194 (entity) => entity.action, entity => ({}), false), 194 (entity) => entity.action, entity => ({}), false),
195 - new EntityTableColumn<Event>('entityId', 'event.entityId', '100%', 195 + new EntityTableColumn<Event>('entityId', 'edge.entity-id', '100%',
196 (entity) => entity.id.id, entity => ({}), false), //TODO: replace this to entity.entityId because of conflict wiht entityId model 196 (entity) => entity.id.id, entity => ({}), false), //TODO: replace this to entity.entityId because of conflict wiht entityId model
197 new EntityTableColumn<Event>('status', 'event.status', '100%', 197 new EntityTableColumn<Event>('status', 'event.status', '100%',
198 (entity) => this.updateEdgeEventStatus(entity.createdTime), 198 (entity) => this.updateEdgeEventStatus(entity.createdTime),
@@ -36,6 +36,12 @@ @@ -36,6 +36,12 @@
36 </button> 36 </button>
37 <button mat-raised-button color="primary" 37 <button mat-raised-button color="primary"
38 [disabled]="(isLoading$ | async)" 38 [disabled]="(isLoading$ | async)"
  39 + (click)="onEntityAction($event, 'unassignFromEdge')"
  40 + [fxShow]="!isEdit && assetScope === 'edge'">
  41 + {{ 'edge.unassign-from-edge' | translate }}
  42 + </button>
  43 + <button mat-raised-button color="primary"
  44 + [disabled]="(isLoading$ | async)"
39 (click)="onEntityAction($event, 'delete')" 45 (click)="onEntityAction($event, 'delete')"
40 [fxShow]="!hideDelete() && !isEdit"> 46 [fxShow]="!hideDelete() && !isEdit">
41 {{'asset.delete' | translate }} 47 {{'asset.delete' | translate }}
@@ -466,6 +466,9 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse @@ -466,6 +466,9 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
466 case 'unassignFromCustomer': 466 case 'unassignFromCustomer':
467 this.unassignFromCustomer(action.event, action.entity); 467 this.unassignFromCustomer(action.event, action.entity);
468 return true; 468 return true;
  469 + case 'unassignFromEdge':
  470 + this.unassignFromEdge(action.event, action.entity);
  471 + return true;
469 } 472 }
470 return false; 473 return false;
471 } 474 }
@@ -55,6 +55,12 @@ @@ -55,6 +55,12 @@
55 </button> 55 </button>
56 <button mat-raised-button color="primary" 56 <button mat-raised-button color="primary"
57 [disabled]="(isLoading$ | async)" 57 [disabled]="(isLoading$ | async)"
  58 + (click)="onEntityAction($event, 'unassignFromEdge')"
  59 + [fxShow]="!isEdit && dashboardScope === 'edge'">
  60 + {{ 'edge.unassign-from-edge' | translate }}
  61 + </button>
  62 + <button mat-raised-button color="primary"
  63 + [disabled]="(isLoading$ | async)"
58 (click)="onEntityAction($event, 'delete')" 64 (click)="onEntityAction($event, 'delete')"
59 [fxShow]="!hideDelete() && !isEdit"> 65 [fxShow]="!hideDelete() && !isEdit">
60 {{'dashboard.delete' | translate }} 66 {{'dashboard.delete' | translate }}
@@ -250,6 +250,12 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< @@ -250,6 +250,12 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
250 if (dashboardScope === 'edge') { 250 if (dashboardScope === 'edge') {
251 actions.push( 251 actions.push(
252 { 252 {
  253 + name: this.translate.instant('dashboard.export'),
  254 + icon: 'file_download',
  255 + isEnabled: () => true,
  256 + onAction: ($event, entity) => this.exportDashboard($event, entity)
  257 + },
  258 + {
253 name: this.translate.instant('edge.unassign-from-edge'), 259 name: this.translate.instant('edge.unassign-from-edge'),
254 icon: 'portable_wifi_off', 260 icon: 'portable_wifi_off',
255 isEnabled: (entity) => true, 261 isEnabled: (entity) => true,
@@ -351,7 +357,10 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< @@ -351,7 +357,10 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
351 } 357 }
352 if (this.config.componentsData.dashboardScope === 'customer') { 358 if (this.config.componentsData.dashboardScope === 'customer') {
353 this.router.navigateByUrl(`customers/${this.config.componentsData.customerId}/dashboards/${dashboard.id.id}`); 359 this.router.navigateByUrl(`customers/${this.config.componentsData.customerId}/dashboards/${dashboard.id.id}`);
354 - } else { 360 + } else if (this.config.componentsData.dashboardScope === 'edge') {
  361 + this.router.navigateByUrl(`edges/${this.config.componentsData.edgeId}/dashboards/${dashboard.id.id}`);
  362 + }
  363 + else {
355 this.router.navigateByUrl(`dashboards/${dashboard.id.id}`); 364 this.router.navigateByUrl(`dashboards/${dashboard.id.id}`);
356 } 365 }
357 } 366 }
@@ -543,6 +552,9 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< @@ -543,6 +552,9 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
543 case 'unassignFromCustomer': 552 case 'unassignFromCustomer':
544 this.unassignFromCustomer(action.event, action.entity, this.config.componentsData.customerId); 553 this.unassignFromCustomer(action.event, action.entity, this.config.componentsData.customerId);
545 return true; 554 return true;
  555 + case 'unassignFromEdge':
  556 + this.unassignFromEdge(action.event, action.entity);
  557 + return true;
546 } 558 }
547 return false; 559 return false;
548 } 560 }
@@ -572,7 +584,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< @@ -572,7 +584,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
572 $event.stopPropagation(); 584 $event.stopPropagation();
573 } 585 }
574 this.dialogService.confirm( 586 this.dialogService.confirm(
575 - this.translate.instant('dashboard.unassign-dashboard-from-edge-title', {dashboardName: dashboard.name}), 587 + this.translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title}),
576 this.translate.instant('dashboard.unassign-dashboard-from-edge-text'), 588 this.translate.instant('dashboard.unassign-dashboard-from-edge-text'),
577 this.translate.instant('action.no'), 589 this.translate.instant('action.no'),
578 this.translate.instant('action.yes'), 590 this.translate.instant('action.yes'),
@@ -40,6 +40,12 @@ @@ -40,6 +40,12 @@
40 [fxShow]="!isEdit"> 40 [fxShow]="!isEdit">
41 {{ (deviceScope === 'customer_user' ? 'device.view-credentials' : 'device.manage-credentials') | translate }} 41 {{ (deviceScope === 'customer_user' ? 'device.view-credentials' : 'device.manage-credentials') | translate }}
42 </button> 42 </button>
  43 + <button mat-raised-button color="primary"
  44 + [disabled]="(isLoading$ | async)"
  45 + (click)="onEntityAction($event, 'unassignFromEdge')"
  46 + [fxShow]="!isEdit && deviceScope === 'edge'">
  47 + {{ 'edge.unassign-from-edge' | translate }}
  48 + </button>
43 <button mat-raised-button color="primary" fxFlex.xs 49 <button mat-raised-button color="primary" fxFlex.xs
44 [disabled]="(isLoading$ | async)" 50 [disabled]="(isLoading$ | async)"
45 (click)="onEntityAction($event, 'delete')" 51 (click)="onEntityAction($event, 'delete')"
@@ -544,6 +544,9 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev @@ -544,6 +544,9 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
544 case 'unassignFromCustomer': 544 case 'unassignFromCustomer':
545 this.unassignFromCustomer(action.event, action.entity); 545 this.unassignFromCustomer(action.event, action.entity);
546 return true; 546 return true;
  547 + case 'unassignFromEdge':
  548 + this.unassignFromEdge(action.event, action.entity);
  549 + return true;
547 case 'manageCredentials': 550 case 'manageCredentials':
548 this.manageCredentials(action.event, action.entity); 551 this.manageCredentials(action.event, action.entity);
549 return true; 552 return true;
@@ -24,6 +24,9 @@ import { DevicesTableConfigResolver } from "@home/pages/device/devices-table-con @@ -24,6 +24,9 @@ import { DevicesTableConfigResolver } from "@home/pages/device/devices-table-con
24 import { EntityViewsTableConfigResolver } from "@home/pages/entity-view/entity-views-table-config.resolver"; 24 import { EntityViewsTableConfigResolver } from "@home/pages/entity-view/entity-views-table-config.resolver";
25 import { DashboardsTableConfigResolver } from "@home/pages/dashboard/dashboards-table-config.resolver"; 25 import { DashboardsTableConfigResolver } from "@home/pages/dashboard/dashboards-table-config.resolver";
26 import { RuleChainsTableConfigResolver } from "@home/pages/rulechain/rulechains-table-config.resolver"; 26 import { RuleChainsTableConfigResolver } from "@home/pages/rulechain/rulechains-table-config.resolver";
  27 +import { DashboardPageComponent } from "@home/pages/dashboard/dashboard-page.component";
  28 +import { dashboardBreadcumbLabelFunction, DashboardResolver } from "@home/pages/dashboard/dashboard-routing.module";
  29 +import { BreadCrumbConfig } from "@shared/components/breadcrumb";
27 30
28 const routes: Routes = [ 31 const routes: Routes = [
29 { 32 {
@@ -53,7 +56,7 @@ const routes: Routes = [ @@ -53,7 +56,7 @@ const routes: Routes = [
53 auth: [Authority.TENANT_ADMIN], 56 auth: [Authority.TENANT_ADMIN],
54 ruleChainsType: 'edge', 57 ruleChainsType: 'edge',
55 breadcrumb: { 58 breadcrumb: {
56 - label: 'edge.rulechains', 59 + label: 'rulechain.edge-rulechains',
57 icon: 'settings_ethernet' 60 icon: 'settings_ethernet'
58 }, 61 },
59 }, 62 },
@@ -108,19 +111,43 @@ const routes: Routes = [ @@ -108,19 +111,43 @@ const routes: Routes = [
108 }, 111 },
109 { 112 {
110 path: ':edgeId/dashboards', 113 path: ':edgeId/dashboards',
111 - component: EntitiesTableComponent,  
112 data: { 114 data: {
113 - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],  
114 - dashboardsType: 'edge',  
115 breadcrumb: { 115 breadcrumb: {
116 label: 'edge.dashboards', 116 label: 'edge.dashboards',
117 icon: 'dashboard' 117 icon: 'dashboard'
118 } 118 }
119 }, 119 },
120 - resolve: {  
121 - entitiesTableConfig: DashboardsTableConfigResolver  
122 - }  
123 - }] 120 + children: [
  121 + {
  122 + path: '',
  123 + component: EntitiesTableComponent,
  124 + data: {
  125 + auth: [Authority.TENANT_ADMIN],
  126 + dashboardsType: 'edge'
  127 + },
  128 + resolve: {
  129 + entitiesTableConfig: DashboardsTableConfigResolver
  130 + },
  131 + },
  132 + {
  133 + path: ':dashboardId',
  134 + component: DashboardPageComponent,
  135 + data: {
  136 + breadcrumb: {
  137 + labelFunction: dashboardBreadcumbLabelFunction,
  138 + icon: 'dashboard'
  139 + } as BreadCrumbConfig<DashboardPageComponent>,
  140 + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
  141 + title: 'edge.dashboard',
  142 + widgetEditMode: false
  143 + },
  144 + resolve: {
  145 + dashboard: DashboardResolver
  146 + }
  147 + }
  148 + ]
  149 + },
  150 + ]
124 }] 151 }]
125 152
126 @NgModule({ 153 @NgModule({
@@ -103,7 +103,8 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI @@ -103,7 +103,8 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
103 resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<EdgeInfo>> { 103 resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<EdgeInfo>> {
104 const routeParams = route.params; 104 const routeParams = route.params;
105 this.config.componentsData = { 105 this.config.componentsData = {
106 - edgeScope: route.data.edgesType 106 + edgeScope: route.data.edgesType,
  107 + edgeType: ''
107 }; 108 };
108 this.customerId = routeParams.customerId; 109 this.customerId = routeParams.customerId;
109 return this.store.pipe(select(selectAuthUser), take(1)).pipe( 110 return this.store.pipe(select(selectAuthUser), take(1)).pipe(
@@ -152,7 +153,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI @@ -152,7 +153,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
152 new EntityTableColumn<EdgeInfo>('customerIsPublic', 'edge.public', '60px', 153 new EntityTableColumn<EdgeInfo>('customerIsPublic', 'edge.public', '60px',
153 entity => { 154 entity => {
154 return checkBoxCell(entity.customerIsPublic); 155 return checkBoxCell(entity.customerIsPublic);
155 - }, () => ({}), false), 156 + }, () => ({}), false)
156 ); 157 );
157 } 158 }
158 return columns; 159 return columns;
@@ -161,7 +162,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI @@ -161,7 +162,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
161 configureEntityFunctions(edgeScope: string): void { 162 configureEntityFunctions(edgeScope: string): void {
162 if (edgeScope === 'tenant') { 163 if (edgeScope === 'tenant') {
163 this.config.entitiesFetchFunction = pageLink => 164 this.config.entitiesFetchFunction = pageLink =>
164 - this.edgeService.getTenantEdgeInfos(pageLink); 165 + this.edgeService.getTenantEdgeInfos(pageLink, this.config.componentsData.edgeType);
165 this.config.deleteEntity = id => this.edgeService.deleteEdge(id.id); 166 this.config.deleteEntity = id => this.edgeService.deleteEdge(id.id);
166 } 167 }
167 if (edgeScope === 'customer') { 168 if (edgeScope === 'customer') {
@@ -176,10 +177,10 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI @@ -176,10 +177,10 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
176 if (edgeScope === 'tenant') { 177 if (edgeScope === 'tenant') {
177 actions.push( 178 actions.push(
178 { 179 {
179 - name: this.translate.instant('edge.make-public'),  
180 - icon: 'share',  
181 - isEnabled: (entity) => (!entity.customerId || entity.customerId.id === NULL_UUID),  
182 - onAction: ($event, entity) => this.makePublic($event, entity) 180 + name: this.translate.instant('edge.make-public'),
  181 + icon: 'share',
  182 + isEnabled: (entity) => (!entity.customerId || entity.customerId.id === NULL_UUID),
  183 + onAction: ($event, entity) => this.makePublic($event, entity)
183 }, 184 },
184 { 185 {
185 name: this.translate.instant('edge.assign-to-customer'), 186 name: this.translate.instant('edge.assign-to-customer'),
@@ -36,6 +36,12 @@ @@ -36,6 +36,12 @@
36 </button> 36 </button>
37 <button mat-raised-button color="primary" 37 <button mat-raised-button color="primary"
38 [disabled]="(isLoading$ | async)" 38 [disabled]="(isLoading$ | async)"
  39 + (click)="onEntityAction($event, 'unassignFromEdge')"
  40 + [fxShow]="!isEdit && entityViewScope === 'edge'">
  41 + {{ 'edge.unassign-from-edge' | translate }}
  42 + </button>
  43 + <button mat-raised-button color="primary"
  44 + [disabled]="(isLoading$ | async)"
39 (click)="onEntityAction($event, 'delete')" 45 (click)="onEntityAction($event, 'delete')"
40 [fxShow]="!hideDelete() && !isEdit"> 46 [fxShow]="!hideDelete() && !isEdit">
41 {{'entity-view.delete' | translate }} 47 {{'entity-view.delete' | translate }}
@@ -441,6 +441,9 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig @@ -441,6 +441,9 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
441 case 'unassignFromCustomer': 441 case 'unassignFromCustomer':
442 this.unassignFromCustomer(action.event, action.entity); 442 this.unassignFromCustomer(action.event, action.entity);
443 return true; 443 return true;
  444 + case 'unassignFromEdge':
  445 + this.unassignFromEdge(action.event, action.entity);
  446 + return true;
444 } 447 }
445 return false; 448 return false;
446 } 449 }
@@ -210,7 +210,7 @@ const routes: Routes = [ @@ -210,7 +210,7 @@ const routes: Routes = [
210 component: EntitiesTableComponent, 210 component: EntitiesTableComponent,
211 data: { 211 data: {
212 auth: [Authority.TENANT_ADMIN], 212 auth: [Authority.TENANT_ADMIN],
213 - title: 'edge.rulechains', 213 + title: 'rulechain.edge-rulechains',
214 ruleChainsType: 'edges' 214 ruleChainsType: 'edges'
215 }, 215 },
216 resolve: { 216 resolve: {
@@ -227,7 +227,7 @@ const routes: Routes = [ @@ -227,7 +227,7 @@ const routes: Routes = [
227 icon: 'settings_ethernet' 227 icon: 'settings_ethernet'
228 } as BreadCrumbConfig<RuleChainPageComponent>, 228 } as BreadCrumbConfig<RuleChainPageComponent>,
229 auth: [Authority.TENANT_ADMIN], 229 auth: [Authority.TENANT_ADMIN],
230 - title: 'edge.rulechain', 230 + title: 'rulechain.edge-rulechain',
231 import: false, 231 import: false,
232 ruleChainType: ruleChainType.edge 232 ruleChainType: ruleChainType.edge
233 }, 233 },
@@ -248,7 +248,7 @@ const routes: Routes = [ @@ -248,7 +248,7 @@ const routes: Routes = [
248 icon: 'settings_ethernet' 248 icon: 'settings_ethernet'
249 } as BreadCrumbConfig<RuleChainPageComponent>, 249 } as BreadCrumbConfig<RuleChainPageComponent>,
250 auth: [Authority.TENANT_ADMIN], 250 auth: [Authority.TENANT_ADMIN],
251 - title: 'edge.rulechain', 251 + title: 'rulechain.edge-rulechain',
252 import: true, 252 import: true,
253 ruleChainType: ruleChainType.edge 253 ruleChainType: ruleChainType.edge
254 }, 254 },
@@ -31,7 +31,19 @@ @@ -31,7 +31,19 @@
31 <button mat-raised-button color="primary" 31 <button mat-raised-button color="primary"
32 [disabled]="(isLoading$ | async)" 32 [disabled]="(isLoading$ | async)"
33 (click)="onEntityAction($event, 'setRoot')" 33 (click)="onEntityAction($event, 'setRoot')"
34 - [fxShow]="!isEdit && !entity?.root"> 34 + [fxShow]="!isEdit && !entity?.root && ruleChainScope === 'tenant'">
  35 + {{'rulechain.set-root' | translate }}
  36 + </button>
  37 + <button mat-raised-button color="primary"
  38 + [disabled]="(isLoading$ | async)"
  39 + (click)="onEntityAction($event, 'setDefaultRoot')"
  40 + [fxShow]="!isEdit && !entity?.root && ruleChainScope === 'edges'">
  41 + {{'rulechain.set-default-root-edge' | translate }}
  42 + </button>
  43 + <button mat-raised-button color="primary"
  44 + [disabled]="(isLoading$ | async)"
  45 + (click)="onEntityAction($event, 'setRoot')"
  46 + [fxShow]="!isEdit && !isRootRuleChain() && ruleChainScope === 'edge'">
35 {{'rulechain.set-root' | translate }} 47 {{'rulechain.set-root' | translate }}
36 </button> 48 </button>
37 <button mat-raised-button color="primary" 49 <button mat-raised-button color="primary"
@@ -31,6 +31,8 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod @@ -31,6 +31,8 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod
31 }) 31 })
32 export class RuleChainComponent extends EntityComponent<RuleChain> { 32 export class RuleChainComponent extends EntityComponent<RuleChain> {
33 33
  34 + ruleChainScope: 'tenant' | 'edges' | 'edge';
  35 +
34 constructor(protected store: Store<AppState>, 36 constructor(protected store: Store<AppState>,
35 protected translate: TranslateService, 37 protected translate: TranslateService,
36 @Inject('entity') protected entityValue: RuleChain, 38 @Inject('entity') protected entityValue: RuleChain,
@@ -39,6 +41,11 @@ export class RuleChainComponent extends EntityComponent<RuleChain> { @@ -39,6 +41,11 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
39 super(store, fb, entityValue, entitiesTableConfigValue); 41 super(store, fb, entityValue, entitiesTableConfigValue);
40 } 42 }
41 43
  44 + ngOnInit() {
  45 + this.ruleChainScope = this.entitiesTableConfig.componentsData.ruleChainScope;
  46 + super.ngOnInit();
  47 + }
  48 +
42 hideDelete() { 49 hideDelete() {
43 if (this.entitiesTableConfig) { 50 if (this.entitiesTableConfig) {
44 return !this.entitiesTableConfig.deleteEnabled(this.entity); 51 return !this.entitiesTableConfig.deleteEnabled(this.entity);
@@ -78,4 +85,12 @@ export class RuleChainComponent extends EntityComponent<RuleChain> { @@ -78,4 +85,12 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
78 horizontalPosition: 'right' 85 horizontalPosition: 'right'
79 })); 86 }));
80 } 87 }
  88 +
  89 + isRootRuleChain() {
  90 + if (this.entitiesTableConfig && this.entityValue) {
  91 + return this.entitiesTableConfig.componentsData.rootRuleChainId == this.entityValue.id.id;
  92 + } else {
  93 + return false;
  94 + }
  95 + }
81 } 96 }
@@ -20,7 +20,7 @@ import {ActivatedRouteSnapshot, Resolve, Route, Router} from '@angular/router'; @@ -20,7 +20,7 @@ import {ActivatedRouteSnapshot, Resolve, Route, Router} from '@angular/router';
20 import { 20 import {
21 CellActionDescriptor, 21 CellActionDescriptor,
22 checkBoxCell, 22 checkBoxCell,
23 - DateEntityTableColumn, 23 + DateEntityTableColumn, EntityColumn,
24 EntityTableColumn, 24 EntityTableColumn,
25 EntityTableConfig, 25 EntityTableConfig,
26 GroupActionDescriptor, HeaderActionDescriptor 26 GroupActionDescriptor, HeaderActionDescriptor
@@ -37,7 +37,7 @@ import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.com @@ -37,7 +37,7 @@ import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.com
37 import { ImportExportService } from '@home/components/import-export/import-export.service'; 37 import { ImportExportService } from '@home/components/import-export/import-export.service';
38 import { ItemBufferService } from '@core/services/item-buffer.service'; 38 import { ItemBufferService } from '@core/services/item-buffer.service';
39 import { EdgeService } from "@core/http/edge.service"; 39 import { EdgeService } from "@core/http/edge.service";
40 -import { map } from "rxjs/operators"; 40 +import {map, mergeMap} from "rxjs/operators";
41 import { forkJoin, Observable } from "rxjs"; 41 import { forkJoin, Observable } from "rxjs";
42 import { 42 import {
43 AddEntitiesToEdgeDialogComponent, 43 AddEntitiesToEdgeDialogComponent,
@@ -69,18 +69,6 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -69,18 +69,6 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
69 this.config.entityTranslations = entityTypeTranslations.get(EntityType.RULE_CHAIN); 69 this.config.entityTranslations = entityTypeTranslations.get(EntityType.RULE_CHAIN);
70 this.config.entityResources = entityTypeResources.get(EntityType.RULE_CHAIN); 70 this.config.entityResources = entityTypeResources.get(EntityType.RULE_CHAIN);
71 71
72 - this.config.columns.push(  
73 - new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'),  
74 - new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%'),  
75 - new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px',  
76 - entity => {  
77 - if (isDefined(this.config.componentsData.edgeId)) {  
78 - return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id == entity.id.id));  
79 - } else {  
80 - return checkBoxCell(entity.root);  
81 - }  
82 - })  
83 - );  
84 this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title', 72 this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title',
85 { ruleChainName: ruleChain.name }); 73 { ruleChainName: ruleChain.name });
86 this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text'); 74 this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text');
@@ -98,25 +86,56 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -98,25 +86,56 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
98 ruleChainScope: route.data.ruleChainsType, 86 ruleChainScope: route.data.ruleChainsType,
99 edgeId: routeParams.edgeId 87 edgeId: routeParams.edgeId
100 }; 88 };
101 - if (this.config.componentsData.edgeId) { 89 + this.config.columns = this.configureEntityTableColumns(this.config.componentsData.ruleChainScope);
  90 + this.configureEntityFunctions(this.config.componentsData.ruleChainScope);
  91 + this.config.groupActionDescriptors = this.configureGroupActions(this.config.componentsData.ruleChainScope);
  92 + this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.ruleChainScope);
  93 + this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.ruleChainScope);
  94 + if (this.config.componentsData.ruleChainScope === 'tenant' || this.config.componentsData.ruleChainScope === 'edges') {
  95 + this.config.entitySelectionEnabled = ruleChain => ruleChain && !ruleChain.root;
  96 + this.config.deleteEnabled = (ruleChain) => ruleChain && !ruleChain.root;
  97 + this.config.entitiesDeleteEnabled = true;
  98 + }
  99 + else if (this.config.componentsData.ruleChainScope === 'edge') {
102 this.config.entitySelectionEnabled = ruleChain => this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id; 100 this.config.entitySelectionEnabled = ruleChain => this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id;
103 - this.config.deleteEnabled = () => false;  
104 this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe(edge => { 101 this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe(edge => {
105 this.config.componentsData.edge = edge; 102 this.config.componentsData.edge = edge;
106 this.config.tableTitle = edge.name + ': ' + this.translate.instant('rulechain.edge-rulechains'); 103 this.config.tableTitle = edge.name + ': ' + this.translate.instant('rulechain.edge-rulechains');
107 }); 104 });
  105 + this.config.entitiesDeleteEnabled = false;
108 } 106 }
109 - else {  
110 - this.config.entitySelectionEnabled = ruleChain => ruleChain && !ruleChain.root;  
111 - this.config.deleteEnabled = (ruleChain) => ruleChain && !ruleChain.root;  
112 - }  
113 - this.configureEntityFunctions(this.config.componentsData.ruleChainScope);  
114 - this.config.groupActionDescriptors = this.configureGroupActions(this.config.componentsData.ruleChainScope);  
115 - this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.ruleChainScope);  
116 - this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.ruleChainScope);  
117 return this.config; 107 return this.config;
118 } 108 }
119 109
  110 + configureEntityTableColumns(ruleChainScope: string): Array<EntityColumn<RuleChain>> {
  111 + const columns: Array<EntityColumn<RuleChain>> = [];
  112 + if (ruleChainScope === 'tenant' || ruleChainScope === 'edge') {
  113 + columns.push(
  114 + new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'),
  115 + new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%'),
  116 + new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px',
  117 + entity => {
  118 + if (ruleChainScope === 'edge') {
  119 + return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id == entity.id.id));
  120 + } else {
  121 + return checkBoxCell(entity.root);
  122 + }
  123 + })
  124 + );
  125 + }
  126 + if (ruleChainScope === 'edges') {
  127 + columns.push(
  128 + new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'),
  129 + new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%'),
  130 + new EntityTableColumn<RuleChain>('root', 'rulechain.default-root', '60px',
  131 + entity => {
  132 + return checkBoxCell(entity.root);
  133 + })
  134 + );
  135 + }
  136 + return columns;
  137 + }
  138 +
120 configureAddActions(ruleChainScope: string): Array<HeaderActionDescriptor> { 139 configureAddActions(ruleChainScope: string): Array<HeaderActionDescriptor> {
121 const actions: Array<HeaderActionDescriptor> = []; 140 const actions: Array<HeaderActionDescriptor> = [];
122 if (ruleChainScope === 'tenant' || ruleChainScope === 'edges') { 141 if (ruleChainScope === 'tenant' || ruleChainScope === 'edges') {
@@ -293,8 +312,8 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -293,8 +312,8 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
293 true 312 true
294 ).subscribe((res) => { 313 ).subscribe((res) => {
295 if (res) { 314 if (res) {
296 - if (this.config.componentsData.edgeId) {  
297 - this.edgeService.setRootRuleChain(this.config.componentsData.edgeId, ruleChain.id.id).subscribe( 315 + if (this.config.componentsData.ruleChainScope === 'edge') {
  316 + this.ruleChainService.setEdgeRootRuleChain(this.config.componentsData.edgeId, ruleChain.id.id).subscribe(
298 (edge) => { 317 (edge) => {
299 this.config.componentsData.edge = edge; 318 this.config.componentsData.edge = edge;
300 this.config.table.updateData(); 319 this.config.table.updateData();
@@ -323,6 +342,9 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -323,6 +342,9 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
323 case 'setRoot': 342 case 'setRoot':
324 this.setRootRuleChain(action.event, action.entity); 343 this.setRootRuleChain(action.event, action.entity);
325 return true; 344 return true;
  345 + case 'setDefaultRoot':
  346 + this.setDefaultRootEdgeRuleChain(action.event, action.entity);
  347 + return true;
326 } 348 }
327 return false; 349 return false;
328 } 350 }
@@ -465,18 +487,18 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -465,18 +487,18 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
465 } 487 }
466 488
467 isNonRootRuleChain(ruleChain: RuleChain) { 489 isNonRootRuleChain(ruleChain: RuleChain) {
468 - if (this.config.componentsData.edgeId) { 490 + if (this.config.componentsData.ruleChainScope === 'edge') {
469 return (isDefined(this.config.componentsData.edge.rootRuleChainId) && this.config.componentsData.edge.rootRuleChainId != null && this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id); 491 return (isDefined(this.config.componentsData.edge.rootRuleChainId) && this.config.componentsData.edge.rootRuleChainId != null && this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id);
470 } 492 }
471 return (isDefined(ruleChain)) && !ruleChain.root; 493 return (isDefined(ruleChain)) && !ruleChain.root;
472 } 494 }
473 495
474 isDefaultEdgeRuleChain(ruleChain) { 496 isDefaultEdgeRuleChain(ruleChain) {
475 - return (isDefined(ruleChain)) && !ruleChain.root && ruleChain.isDefault; 497 + return (isDefined(ruleChain)) && !ruleChain.root && this.config.componentsData.defaultEdgeRuleChainIds.includes(ruleChain.id.id);
476 } 498 }
477 499
478 isNonDefaultEdgeRuleChain(ruleChain) { 500 isNonDefaultEdgeRuleChain(ruleChain) {
479 - return (isDefined(ruleChain)) && !ruleChain.root && !ruleChain.isDefault; 501 + return (isDefined(ruleChain)) && !ruleChain.root && !this.config.componentsData.defaultEdgeRuleChainIds.includes(ruleChain.id.id);
480 } 502 }
481 503
482 fetchRuleChains(pageLink: PageLink) { 504 fetchRuleChains(pageLink: PageLink) {
@@ -484,17 +506,10 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< @@ -484,17 +506,10 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
484 } 506 }
485 507
486 fetchEdgeRuleChains(pageLink: PageLink) { 508 fetchEdgeRuleChains(pageLink: PageLink) {
487 - let defaultEdgeRuleChainIds: Array<string> = []; 509 + this.config.componentsData.defaultEdgeRuleChainIds = [];
488 this.ruleChainService.getDefaultEdgeRuleChains().subscribe(ruleChains => { 510 this.ruleChainService.getDefaultEdgeRuleChains().subscribe(ruleChains => {
489 - ruleChains.map(ruleChain => defaultEdgeRuleChainIds.push(ruleChain.id.id)) 511 + ruleChains.map(ruleChain => this.config.componentsData.defaultEdgeRuleChainIds.push(ruleChain.id.id));
490 }); 512 });
491 - return this.ruleChainService.getRuleChains(pageLink, ruleChainType.edge).pipe(  
492 - map(response => {  
493 - response.data.map(ruleChain =>  
494 - ruleChain.isDefault = defaultEdgeRuleChainIds.some(id => ruleChain.id.id.includes(id))  
495 - );  
496 - return response;  
497 - })  
498 - ); 513 + return this.ruleChainService.getRuleChains(pageLink, ruleChainType.edge);
499 } 514 }
500 } 515 }
@@ -143,9 +143,10 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, @@ -143,9 +143,10 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
143 break; 143 break;
144 case EntityType.EDGE: 144 case EntityType.EDGE:
145 this.placeholder = this.required ? this.translate.instant('edge.enter-edge-type') 145 this.placeholder = this.required ? this.translate.instant('edge.enter-edge-type')
146 - : this.translate.instant('edge.edge-any-edge'); 146 + : this.translate.instant('edge.any-edge');
147 this.secondaryPlaceholder = '+' + this.translate.instant('edge.edge-type'); 147 this.secondaryPlaceholder = '+' + this.translate.instant('edge.edge-type');
148 this.noSubtypesMathingText = 'edge.no-edge-types-matching'; 148 this.noSubtypesMathingText = 'edge.no-edge-types-matching';
  149 + this.subtypeListEmptyText = 'edge.edge-type-list-empty';
149 this.broadcastSubscription = this.broadcast.on('edgeSaved', () => { 150 this.broadcastSubscription = this.broadcast.on('edgeSaved', () => {
150 this.entitySubtypes = null; 151 this.entitySubtypes = null;
151 }); 152 });
@@ -33,3 +33,4 @@ export * from './tenant-profile-id'; @@ -33,3 +33,4 @@ export * from './tenant-profile-id';
33 export * from './user-id'; 33 export * from './user-id';
34 export * from './widget-type-id'; 34 export * from './widget-type-id';
35 export * from './widgets-bundle-id'; 35 export * from './widgets-bundle-id';
  36 +export * from './edge-id';
@@ -1179,8 +1179,6 @@ @@ -1179,8 +1179,6 @@
1179 "unassign-from-edge": "Unassign from edge", 1179 "unassign-from-edge": "Unassign from edge",
1180 "dashboards": "Edge Dashboards", 1180 "dashboards": "Edge Dashboards",
1181 "manage-edge-rulechains": "Manage edge rule chains", 1181 "manage-edge-rulechains": "Manage edge rule chains",
1182 - "rulechains": "Edge Rule Chains",  
1183 - "rulechain": "Edge Rule Chain",  
1184 "edge-key": "Edge key", 1182 "edge-key": "Edge key",
1185 "copy-edge-key": "Copy Edge key", 1183 "copy-edge-key": "Copy Edge key",
1186 "edge-key-copied-message": "Edge key has been copied to clipboard", 1184 "edge-key-copied-message": "Edge key has been copied to clipboard",
@@ -1204,6 +1202,9 @@ @@ -1204,6 +1202,9 @@
1204 "enter-edge-type": "Enter entity view type", 1202 "enter-edge-type": "Enter entity view type",
1205 "any-edge": "Any edge", 1203 "any-edge": "Any edge",
1206 "no-edge-types-matching": "No edge types matching '{{entitySubtype}}' were found.", 1204 "no-edge-types-matching": "No edge types matching '{{entitySubtype}}' were found.",
  1205 + "edge-type-list-empty": "No device types selected.",
  1206 + "edge-types": "Edge types",
  1207 + "dashboard": "Edge dashboard",
1207 "unassign-edges-action-title": "Unassign { count, plural, 1 {1 edge} other {# edges} } from customer" 1208 "unassign-edges-action-title": "Unassign { count, plural, 1 {1 edge} other {# edges} } from customer"
1208 }, 1209 },
1209 "error": { 1210 "error": {
@@ -2035,6 +2036,7 @@ @@ -2035,6 +2036,7 @@
2035 "open-rulechain": "Open rule chain", 2036 "open-rulechain": "Open rule chain",
2036 "assign-new-rulechain": "Assign new rulechain", 2037 "assign-new-rulechain": "Assign new rulechain",
2037 "edge-rulechains": "Edge Rule chains", 2038 "edge-rulechains": "Edge Rule chains",
  2039 + "edge-rulechain": "Edge Rule chain",
2038 "core-rulechains": "Core Rule chains", 2040 "core-rulechains": "Core Rule chains",
2039 "unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.", 2041 "unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.",
2040 "unassign-rulechains-from-edge-title": "Unassign { count, plural, 1 {1 rulechain} other {# rulechains} } from edge", 2042 "unassign-rulechains-from-edge-title": "Unassign { count, plural, 1 {1 rulechain} other {# rulechains} } from edge",
@@ -2054,7 +2056,8 @@ @@ -2054,7 +2056,8 @@
2054 "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainName}}'?", 2056 "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainName}}'?",
2055 "unassign-rulechains-title": "Are you sure you want to unassign { count, plural, 1 {1 rulechain} other {# rulechains} }?", 2057 "unassign-rulechains-title": "Are you sure you want to unassign { count, plural, 1 {1 rulechain} other {# rulechains} }?",
2056 "unassign-rulechains": "Unassign rulechains", 2058 "unassign-rulechains": "Unassign rulechains",
2057 - "default": "Default" 2059 + "default": "Default",
  2060 + "default-root": "Default root"
2058 }, 2061 },
2059 "rulenode": { 2062 "rulenode": {
2060 "details": "Details", 2063 "details": "Details",
@@ -194,7 +194,7 @@ export default function EdgeRoutes($stateProvider, types) { @@ -194,7 +194,7 @@ export default function EdgeRoutes($stateProvider, types) {
194 }, 194 },
195 data: { 195 data: {
196 searchEnabled: true, 196 searchEnabled: true,
197 - pageTitle: 'edge.rulechains', 197 + pageTitle: 'rulechain.edge-rulechains',
198 ruleChainsType: 'edge' 198 ruleChainsType: 'edge'
199 }, 199 },
200 ncyBreadcrumb: { 200 ncyBreadcrumb: {