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 492 @PathVariable(EDGE_ID) String strEdgeId,
493 493 @RequestParam int pageSize,
494 494 @RequestParam int page,
  495 + @RequestParam(required = false) String type,
495 496 @RequestParam(required = false) String textSearch,
496 497 @RequestParam(required = false) String sortProperty,
497 498 @RequestParam(required = false) String sortOrder,
... ... @@ -503,7 +504,11 @@ public class AssetController extends BaseController {
503 504 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
504 505 checkEdgeId(edgeId, Operation.READ);
505 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 512 } catch (Exception e) {
508 513 throw handleException(e);
509 514 }
... ...
... ... @@ -677,6 +677,7 @@ public class EntityViewController extends BaseController {
677 677 @PathVariable(EDGE_ID) String strEdgeId,
678 678 @RequestParam int pageSize,
679 679 @RequestParam int page,
  680 + @RequestParam(required = false) String type,
680 681 @RequestParam(required = false) String textSearch,
681 682 @RequestParam(required = false) String sortProperty,
682 683 @RequestParam(required = false) String sortOrder,
... ... @@ -688,7 +689,11 @@ public class EntityViewController extends BaseController {
688 689 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
689 690 checkEdgeId(edgeId, Operation.READ);
690 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 697 } catch (Exception e) {
693 698 throw handleException(e);
694 699 }
... ...
... ... @@ -82,4 +82,6 @@ public interface AssetService {
82 82 Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
83 83
84 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 83 EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
84 84
85 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 177 * @return the list of asset objects
178 178 */
179 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 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 385 private DataValidator<Asset> assetValidator =
376 386 new DataValidator<Asset>() {
377 387
... ...
... ... @@ -165,4 +165,18 @@ public interface EntityViewDao extends Dao<EntityView> {
165 165 UUID edgeId,
166 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 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 403 private DataValidator<EntityView> entityViewValidator =
394 404 new DataValidator<EntityView>() {
395 405
... ...
... ... @@ -176,4 +176,4 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
176 176 edge.setAdditionalInfo(additionalInfo);
177 177 return edge;
178 178 }
179   -}
\ No newline at end of file
  179 +}
... ...
... ... @@ -132,5 +132,16 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity,
132 132 @Param("searchText") String searchText,
133 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 146 Long countByTenantIdAndTypeIsNot(UUID tenantId, String type);
136 147 }
... ...
... ... @@ -200,6 +200,18 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
200 200 }
201 201
202 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 215 public Long countByTenantId(TenantId tenantId) {
204 216 return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE);
205 217 }
... ...
... ... @@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 22 import org.springframework.data.repository.query.Param;
23   -import org.thingsboard.server.dao.model.sql.AssetEntity;
24 23 import org.thingsboard.server.dao.model.sql.DeviceEntity;
25 24 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
26 25
... ...
... ... @@ -129,4 +129,15 @@ public interface EntityViewRepository extends PagingAndSortingRepository<EntityV
129 129 @Param("edgeId") UUID edgeId,
130 130 @Param("searchText") String searchText,
131 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 192 Objects.toString(pageLink.getTextSearch(), ""),
193 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 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 103 config?: RequestConfig): Observable<PageData<AssetInfo>> {
104 104 return this.http.get<PageData<AssetInfo>>(`/api/edge/${edgeId}/assets${pageLink.toQuery()}&type=${type}`,
105 105 defaultHttpOptionsFromConfig(config));
... ...
... ... @@ -76,12 +76,6 @@ export class EdgeService {
76 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 79 public getTenantEdgeInfos(pageLink: PageLink, type: string = '',
86 80 config?: RequestConfig): Observable<PageData<EdgeInfo>> {
87 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 44 import { EntityType } from '@shared/models/entity-type.models';
45 45 import { deepClone, snakeCase } from '@core/utils';
46 46 import { DebugRuleNodeEventBody } from '@app/shared/models/event.models';
  47 +import { Edge } from "@shared/models/edge.models";
47 48
48 49 @Injectable({
49 50 providedIn: 'root'
... ... @@ -323,4 +324,9 @@ export class RuleChainService {
323 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 101 {deviceType});
102 102 }
103 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 115 case AliasFilterType.entityViewType:
105 116 const entityView = this.filter.entityViewType;
106 117 prefix = this.filter.entityViewNameFilter;
... ... @@ -166,6 +177,7 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
166 177 break;
167 178 case AliasFilterType.assetSearchQuery:
168 179 case AliasFilterType.deviceSearchQuery:
  180 + case AliasFilterType.edgeSearchQuery:
169 181 case AliasFilterType.entityViewSearchQuery:
170 182 allEntitiesText = this.translate.instant('alias.all-entities');
171 183 anyRelationText = this.translate.instant('alias.any-relation');
... ... @@ -207,6 +219,16 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
207 219 this.filterDisplayValue = this.translate.instant('alias.filter-type-device-search-query-description',
208 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 232 } else if (this.filter.type === AliasFilterType.entityViewSearchQuery) {
211 233 const entityViewTypesQuoted = [];
212 234 this.filter.entityViewTypes.forEach((filterEntityViewType) => {
... ...
... ... @@ -103,6 +103,16 @@
103 103 <input matInput formControlName="entityViewNameFilter">
104 104 </mat-form-field>
105 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 116 <ng-template [ngSwitchCase]="aliasFilterType.relationsQuery">
107 117 <section fxLayout="column" id="relationsQueryFilter">
108 118 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
... ... @@ -169,6 +179,7 @@
169 179 </ng-template>
170 180 <ng-template [ngSwitchCase]="entityFilterFormGroup.get('type').value === aliasFilterType.assetSearchQuery ||
171 181 entityFilterFormGroup.get('type').value === aliasFilterType.deviceSearchQuery ||
  182 + entityFilterFormGroup.get('type').value === aliasFilterType.edgeSearchQuery ||
172 183 entityFilterFormGroup.get('type').value === aliasFilterType.entityViewSearchQuery ?
173 184 entityFilterFormGroup.get('type').value : ''">
174 185 <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
... ... @@ -248,6 +259,14 @@
248 259 formControlName="deviceTypes">
249 260 </tb-entity-subtype-list>
250 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 270 <ng-template [ngSwitchCase]="aliasFilterType.entityViewSearchQuery">
252 271 <div class="mat-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>entity-view.entity-view-types</div>
253 272 <tb-entity-subtype-list
... ...
... ... @@ -141,6 +141,12 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
141 141 deviceNameFilter: [filter ? filter.deviceNameFilter : '', []],
142 142 });
143 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 150 case AliasFilterType.entityViewType:
145 151 this.filterFormGroup = this.fb.group({
146 152 entityViewType: [filter ? filter.entityViewType : null, [Validators.required]],
... ... @@ -153,6 +159,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
153 159 case AliasFilterType.relationsQuery:
154 160 case AliasFilterType.assetSearchQuery:
155 161 case AliasFilterType.deviceSearchQuery:
  162 + case AliasFilterType.edgeSearchQuery:
156 163 case AliasFilterType.entityViewSearchQuery:
157 164 this.filterFormGroup = this.fb.group({
158 165 rootStateEntity: [filter ? filter.rootStateEntity : false, []],
... ... @@ -179,6 +186,9 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit {
179 186 } else if (type === AliasFilterType.deviceSearchQuery) {
180 187 this.filterFormGroup.addControl('deviceTypes',
181 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 192 } else if (type === AliasFilterType.entityViewSearchQuery) {
183 193 this.filterFormGroup.addControl('entityViewTypes',
184 194 this.fb.control(filter ? filter.entityViewTypes : [], [Validators.required]));
... ...
... ... @@ -190,9 +190,9 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
190 190 this.columns.push(
191 191 new EntityTableColumn<Event>('type', 'event.type', '100%',
192 192 (entity) => entity.type, entity => ({}), false),
193   - new EntityTableColumn<Event>('action', 'event.action', '100%',
  193 + new EntityTableColumn<Event>('action', 'edge.event-action', '100%',
194 194 (entity) => entity.action, entity => ({}), false),
195   - new EntityTableColumn<Event>('entityId', 'event.entityId', '100%',
  195 + new EntityTableColumn<Event>('entityId', 'edge.entity-id', '100%',
196 196 (entity) => entity.id.id, entity => ({}), false), //TODO: replace this to entity.entityId because of conflict wiht entityId model
197 197 new EntityTableColumn<Event>('status', 'event.status', '100%',
198 198 (entity) => this.updateEdgeEventStatus(entity.createdTime),
... ...
... ... @@ -36,6 +36,12 @@
36 36 </button>
37 37 <button mat-raised-button color="primary"
38 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 45 (click)="onEntityAction($event, 'delete')"
40 46 [fxShow]="!hideDelete() && !isEdit">
41 47 {{'asset.delete' | translate }}
... ...
... ... @@ -466,6 +466,9 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
466 466 case 'unassignFromCustomer':
467 467 this.unassignFromCustomer(action.event, action.entity);
468 468 return true;
  469 + case 'unassignFromEdge':
  470 + this.unassignFromEdge(action.event, action.entity);
  471 + return true;
469 472 }
470 473 return false;
471 474 }
... ...
... ... @@ -55,6 +55,12 @@
55 55 </button>
56 56 <button mat-raised-button color="primary"
57 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 64 (click)="onEntityAction($event, 'delete')"
59 65 [fxShow]="!hideDelete() && !isEdit">
60 66 {{'dashboard.delete' | translate }}
... ...
... ... @@ -250,6 +250,12 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
250 250 if (dashboardScope === 'edge') {
251 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 259 name: this.translate.instant('edge.unassign-from-edge'),
254 260 icon: 'portable_wifi_off',
255 261 isEnabled: (entity) => true,
... ... @@ -351,7 +357,10 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
351 357 }
352 358 if (this.config.componentsData.dashboardScope === 'customer') {
353 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 364 this.router.navigateByUrl(`dashboards/${dashboard.id.id}`);
356 365 }
357 366 }
... ... @@ -543,6 +552,9 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
543 552 case 'unassignFromCustomer':
544 553 this.unassignFromCustomer(action.event, action.entity, this.config.componentsData.customerId);
545 554 return true;
  555 + case 'unassignFromEdge':
  556 + this.unassignFromEdge(action.event, action.entity);
  557 + return true;
546 558 }
547 559 return false;
548 560 }
... ... @@ -572,7 +584,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
572 584 $event.stopPropagation();
573 585 }
574 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 588 this.translate.instant('dashboard.unassign-dashboard-from-edge-text'),
577 589 this.translate.instant('action.no'),
578 590 this.translate.instant('action.yes'),
... ...
... ... @@ -40,6 +40,12 @@
40 40 [fxShow]="!isEdit">
41 41 {{ (deviceScope === 'customer_user' ? 'device.view-credentials' : 'device.manage-credentials') | translate }}
42 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 49 <button mat-raised-button color="primary" fxFlex.xs
44 50 [disabled]="(isLoading$ | async)"
45 51 (click)="onEntityAction($event, 'delete')"
... ...
... ... @@ -544,6 +544,9 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
544 544 case 'unassignFromCustomer':
545 545 this.unassignFromCustomer(action.event, action.entity);
546 546 return true;
  547 + case 'unassignFromEdge':
  548 + this.unassignFromEdge(action.event, action.entity);
  549 + return true;
547 550 case 'manageCredentials':
548 551 this.manageCredentials(action.event, action.entity);
549 552 return true;
... ...
... ... @@ -24,6 +24,9 @@ import { DevicesTableConfigResolver } from "@home/pages/device/devices-table-con
24 24 import { EntityViewsTableConfigResolver } from "@home/pages/entity-view/entity-views-table-config.resolver";
25 25 import { DashboardsTableConfigResolver } from "@home/pages/dashboard/dashboards-table-config.resolver";
26 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 31 const routes: Routes = [
29 32 {
... ... @@ -53,7 +56,7 @@ const routes: Routes = [
53 56 auth: [Authority.TENANT_ADMIN],
54 57 ruleChainsType: 'edge',
55 58 breadcrumb: {
56   - label: 'edge.rulechains',
  59 + label: 'rulechain.edge-rulechains',
57 60 icon: 'settings_ethernet'
58 61 },
59 62 },
... ... @@ -108,19 +111,43 @@ const routes: Routes = [
108 111 },
109 112 {
110 113 path: ':edgeId/dashboards',
111   - component: EntitiesTableComponent,
112 114 data: {
113   - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
114   - dashboardsType: 'edge',
115 115 breadcrumb: {
116 116 label: 'edge.dashboards',
117 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 153 @NgModule({
... ...
... ... @@ -103,7 +103,8 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
103 103 resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<EdgeInfo>> {
104 104 const routeParams = route.params;
105 105 this.config.componentsData = {
106   - edgeScope: route.data.edgesType
  106 + edgeScope: route.data.edgesType,
  107 + edgeType: ''
107 108 };
108 109 this.customerId = routeParams.customerId;
109 110 return this.store.pipe(select(selectAuthUser), take(1)).pipe(
... ... @@ -152,7 +153,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
152 153 new EntityTableColumn<EdgeInfo>('customerIsPublic', 'edge.public', '60px',
153 154 entity => {
154 155 return checkBoxCell(entity.customerIsPublic);
155   - }, () => ({}), false),
  156 + }, () => ({}), false)
156 157 );
157 158 }
158 159 return columns;
... ... @@ -161,7 +162,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
161 162 configureEntityFunctions(edgeScope: string): void {
162 163 if (edgeScope === 'tenant') {
163 164 this.config.entitiesFetchFunction = pageLink =>
164   - this.edgeService.getTenantEdgeInfos(pageLink);
  165 + this.edgeService.getTenantEdgeInfos(pageLink, this.config.componentsData.edgeType);
165 166 this.config.deleteEntity = id => this.edgeService.deleteEdge(id.id);
166 167 }
167 168 if (edgeScope === 'customer') {
... ... @@ -176,10 +177,10 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
176 177 if (edgeScope === 'tenant') {
177 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 186 name: this.translate.instant('edge.assign-to-customer'),
... ...
... ... @@ -36,6 +36,12 @@
36 36 </button>
37 37 <button mat-raised-button color="primary"
38 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 45 (click)="onEntityAction($event, 'delete')"
40 46 [fxShow]="!hideDelete() && !isEdit">
41 47 {{'entity-view.delete' | translate }}
... ...
... ... @@ -441,6 +441,9 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
441 441 case 'unassignFromCustomer':
442 442 this.unassignFromCustomer(action.event, action.entity);
443 443 return true;
  444 + case 'unassignFromEdge':
  445 + this.unassignFromEdge(action.event, action.entity);
  446 + return true;
444 447 }
445 448 return false;
446 449 }
... ...
... ... @@ -210,7 +210,7 @@ const routes: Routes = [
210 210 component: EntitiesTableComponent,
211 211 data: {
212 212 auth: [Authority.TENANT_ADMIN],
213   - title: 'edge.rulechains',
  213 + title: 'rulechain.edge-rulechains',
214 214 ruleChainsType: 'edges'
215 215 },
216 216 resolve: {
... ... @@ -227,7 +227,7 @@ const routes: Routes = [
227 227 icon: 'settings_ethernet'
228 228 } as BreadCrumbConfig<RuleChainPageComponent>,
229 229 auth: [Authority.TENANT_ADMIN],
230   - title: 'edge.rulechain',
  230 + title: 'rulechain.edge-rulechain',
231 231 import: false,
232 232 ruleChainType: ruleChainType.edge
233 233 },
... ... @@ -248,7 +248,7 @@ const routes: Routes = [
248 248 icon: 'settings_ethernet'
249 249 } as BreadCrumbConfig<RuleChainPageComponent>,
250 250 auth: [Authority.TENANT_ADMIN],
251   - title: 'edge.rulechain',
  251 + title: 'rulechain.edge-rulechain',
252 252 import: true,
253 253 ruleChainType: ruleChainType.edge
254 254 },
... ...
... ... @@ -31,7 +31,19 @@
31 31 <button mat-raised-button color="primary"
32 32 [disabled]="(isLoading$ | async)"
33 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 47 {{'rulechain.set-root' | translate }}
36 48 </button>
37 49 <button mat-raised-button color="primary"
... ...
... ... @@ -31,6 +31,8 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod
31 31 })
32 32 export class RuleChainComponent extends EntityComponent<RuleChain> {
33 33
  34 + ruleChainScope: 'tenant' | 'edges' | 'edge';
  35 +
34 36 constructor(protected store: Store<AppState>,
35 37 protected translate: TranslateService,
36 38 @Inject('entity') protected entityValue: RuleChain,
... ... @@ -39,6 +41,11 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
39 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 49 hideDelete() {
43 50 if (this.entitiesTableConfig) {
44 51 return !this.entitiesTableConfig.deleteEnabled(this.entity);
... ... @@ -78,4 +85,12 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
78 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 20 import {
21 21 CellActionDescriptor,
22 22 checkBoxCell,
23   - DateEntityTableColumn,
  23 + DateEntityTableColumn, EntityColumn,
24 24 EntityTableColumn,
25 25 EntityTableConfig,
26 26 GroupActionDescriptor, HeaderActionDescriptor
... ... @@ -37,7 +37,7 @@ import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.com
37 37 import { ImportExportService } from '@home/components/import-export/import-export.service';
38 38 import { ItemBufferService } from '@core/services/item-buffer.service';
39 39 import { EdgeService } from "@core/http/edge.service";
40   -import { map } from "rxjs/operators";
  40 +import {map, mergeMap} from "rxjs/operators";
41 41 import { forkJoin, Observable } from "rxjs";
42 42 import {
43 43 AddEntitiesToEdgeDialogComponent,
... ... @@ -69,18 +69,6 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
69 69 this.config.entityTranslations = entityTypeTranslations.get(EntityType.RULE_CHAIN);
70 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 72 this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title',
85 73 { ruleChainName: ruleChain.name });
86 74 this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text');
... ... @@ -98,25 +86,56 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
98 86 ruleChainScope: route.data.ruleChainsType,
99 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 100 this.config.entitySelectionEnabled = ruleChain => this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id;
103   - this.config.deleteEnabled = () => false;
104 101 this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe(edge => {
105 102 this.config.componentsData.edge = edge;
106 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 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 139 configureAddActions(ruleChainScope: string): Array<HeaderActionDescriptor> {
121 140 const actions: Array<HeaderActionDescriptor> = [];
122 141 if (ruleChainScope === 'tenant' || ruleChainScope === 'edges') {
... ... @@ -293,8 +312,8 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
293 312 true
294 313 ).subscribe((res) => {
295 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 317 (edge) => {
299 318 this.config.componentsData.edge = edge;
300 319 this.config.table.updateData();
... ... @@ -323,6 +342,9 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
323 342 case 'setRoot':
324 343 this.setRootRuleChain(action.event, action.entity);
325 344 return true;
  345 + case 'setDefaultRoot':
  346 + this.setDefaultRootEdgeRuleChain(action.event, action.entity);
  347 + return true;
326 348 }
327 349 return false;
328 350 }
... ... @@ -465,18 +487,18 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
465 487 }
466 488
467 489 isNonRootRuleChain(ruleChain: RuleChain) {
468   - if (this.config.componentsData.edgeId) {
  490 + if (this.config.componentsData.ruleChainScope === 'edge') {
469 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 493 return (isDefined(ruleChain)) && !ruleChain.root;
472 494 }
473 495
474 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 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 504 fetchRuleChains(pageLink: PageLink) {
... ... @@ -484,17 +506,10 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
484 506 }
485 507
486 508 fetchEdgeRuleChains(pageLink: PageLink) {
487   - let defaultEdgeRuleChainIds: Array<string> = [];
  509 + this.config.componentsData.defaultEdgeRuleChainIds = [];
488 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 143 break;
144 144 case EntityType.EDGE:
145 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 147 this.secondaryPlaceholder = '+' + this.translate.instant('edge.edge-type');
148 148 this.noSubtypesMathingText = 'edge.no-edge-types-matching';
  149 + this.subtypeListEmptyText = 'edge.edge-type-list-empty';
149 150 this.broadcastSubscription = this.broadcast.on('edgeSaved', () => {
150 151 this.entitySubtypes = null;
151 152 });
... ...
... ... @@ -33,3 +33,4 @@ export * from './tenant-profile-id';
33 33 export * from './user-id';
34 34 export * from './widget-type-id';
35 35 export * from './widgets-bundle-id';
  36 +export * from './edge-id';
... ...
... ... @@ -1179,8 +1179,6 @@
1179 1179 "unassign-from-edge": "Unassign from edge",
1180 1180 "dashboards": "Edge Dashboards",
1181 1181 "manage-edge-rulechains": "Manage edge rule chains",
1182   - "rulechains": "Edge Rule Chains",
1183   - "rulechain": "Edge Rule Chain",
1184 1182 "edge-key": "Edge key",
1185 1183 "copy-edge-key": "Copy Edge key",
1186 1184 "edge-key-copied-message": "Edge key has been copied to clipboard",
... ... @@ -1204,6 +1202,9 @@
1204 1202 "enter-edge-type": "Enter entity view type",
1205 1203 "any-edge": "Any edge",
1206 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 1208 "unassign-edges-action-title": "Unassign { count, plural, 1 {1 edge} other {# edges} } from customer"
1208 1209 },
1209 1210 "error": {
... ... @@ -2035,6 +2036,7 @@
2035 2036 "open-rulechain": "Open rule chain",
2036 2037 "assign-new-rulechain": "Assign new rulechain",
2037 2038 "edge-rulechains": "Edge Rule chains",
  2039 + "edge-rulechain": "Edge Rule chain",
2038 2040 "core-rulechains": "Core Rule chains",
2039 2041 "unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.",
2040 2042 "unassign-rulechains-from-edge-title": "Unassign { count, plural, 1 {1 rulechain} other {# rulechains} } from edge",
... ... @@ -2054,7 +2056,8 @@
2054 2056 "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainName}}'?",
2055 2057 "unassign-rulechains-title": "Are you sure you want to unassign { count, plural, 1 {1 rulechain} other {# rulechains} }?",
2056 2058 "unassign-rulechains": "Unassign rulechains",
2057   - "default": "Default"
  2059 + "default": "Default",
  2060 + "default-root": "Default root"
2058 2061 },
2059 2062 "rulenode": {
2060 2063 "details": "Details",
... ...
... ... @@ -194,7 +194,7 @@ export default function EdgeRoutes($stateProvider, types) {
194 194 },
195 195 data: {
196 196 searchEnabled: true,
197   - pageTitle: 'edge.rulechains',
  197 + pageTitle: 'rulechain.edge-rulechains',
198 198 ruleChainsType: 'edge'
199 199 },
200 200 ncyBreadcrumb: {
... ...