Browse Source

UI: Implement Entity relations management.

pull/154/head
Igor Kulikov 9 years ago
parent
commit
ecb5c50d61
  1. 46
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  2. 9
      common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java
  3. 65
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  4. 2
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
  5. 13
      ui/src/app/api/entity-relation.service.js
  6. 6
      ui/src/app/customer/customers.tpl.html
  7. 6
      ui/src/app/device/devices.tpl.html
  8. 2
      ui/src/app/entity/entity-filter.tpl.html
  9. 3
      ui/src/app/entity/entity-select.tpl.html
  10. 5
      ui/src/app/entity/entity-type-select.directive.js
  11. 8
      ui/src/app/entity/entity-type-select.tpl.html
  12. 2
      ui/src/app/entity/index.js
  13. 15
      ui/src/app/entity/relation/add-relation-dialog.controller.js
  14. 17
      ui/src/app/entity/relation/add-relation-dialog.tpl.html
  15. 104
      ui/src/app/entity/relation/relation-table.directive.js
  16. 42
      ui/src/app/entity/relation/relation-table.tpl.html
  17. 86
      ui/src/app/entity/relation/relation-type-autocomplete.directive.js
  18. 25
      ui/src/app/entity/relation/relation-type-autocomplete.scss
  19. 40
      ui/src/app/entity/relation/relation-type-autocomplete.tpl.html
  20. 33
      ui/src/app/locale/locale.constant.js
  21. 6
      ui/src/app/plugin/plugins.tpl.html
  22. 6
      ui/src/app/rule/rules.tpl.html
  23. 6
      ui/src/app/tenant/tenants.tpl.html
  24. 1
      ui/src/scss/main.scss

46
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -34,7 +34,7 @@ import java.util.List;
@RequestMapping("/api") @RequestMapping("/api")
public class EntityRelationController extends BaseController { public class EntityRelationController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.POST) @RequestMapping(value = "/relation", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
@ -42,31 +42,33 @@ public class EntityRelationController extends BaseController {
checkNotNull(relation); checkNotNull(relation);
checkEntityId(relation.getFrom()); checkEntityId(relation.getFrom());
checkEntityId(relation.getTo()); checkEntityId(relation.getTo());
if (relation.getTypeGroup() == null) {
relation.setTypeGroup(RelationTypeGroup.COMMON);
}
relationService.saveRelation(relation).get(); relationService.saveRelation(relation).get();
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"}) @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"})
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public void deleteRelation(@RequestParam("fromId") String strFromId, public void deleteRelation(@RequestParam("fromId") String strFromId,
@RequestParam("fromType") String strFromType, @RequestParam("fromType") String strFromType,
@RequestParam("relationType") String strRelationType, @RequestParam("relationType") String strRelationType,
@RequestParam("relationTypeGroup") String strRelationTypeGroup, @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
@RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
checkParameter("fromId", strFromId); checkParameter("fromId", strFromId);
checkParameter("fromType", strFromType); checkParameter("fromType", strFromType);
checkParameter("relationType", strRelationType); checkParameter("relationType", strRelationType);
checkParameter("relationTypeGroup", strRelationTypeGroup);
checkParameter("toId", strToId); checkParameter("toId", strToId);
checkParameter("toType", strToType); checkParameter("toType", strToType);
EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(fromId); checkEntityId(fromId);
checkEntityId(toId); checkEntityId(toId);
RelationTypeGroup relationTypeGroup = RelationTypeGroup.valueOf(strRelationTypeGroup); RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get(); Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get();
if (!found) { if (!found) {
@ -77,7 +79,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"}) @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"})
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public void deleteRelations(@RequestParam("entityId") String strId, public void deleteRelations(@RequestParam("entityId") String strId,
@ -93,7 +95,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"}) @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"})
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public void checkRelation(@RequestParam("fromId") String strFromId, public void checkRelation(@RequestParam("fromId") String strFromId,
@ -121,7 +123,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"}) @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"})
@ResponseBody @ResponseBody
public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId,
@ -139,7 +141,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"})
@ResponseBody @ResponseBody
public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId,
@ -157,7 +159,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
@ResponseBody @ResponseBody
public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId,
@ -177,7 +179,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"}) @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"})
@ResponseBody @ResponseBody
public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, public List<EntityRelation> findByTo(@RequestParam("toId") String strToId,
@ -195,7 +197,25 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"toId", "toType"})
@ResponseBody
public List<EntityRelationInfo> findInfoByTo(@RequestParam("toId") String strToId,
@RequestParam("toType") String strToType,
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter("toId", strToId);
checkParameter("toType", strToType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(entityId);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try {
return checkNotNull(relationService.findInfoByTo(entityId, typeGroup).get());
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"}) @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"})
@ResponseBody @ResponseBody
public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, public List<EntityRelation> findByTo(@RequestParam("toId") String strToId,
@ -215,7 +235,7 @@ public class EntityRelationController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.POST) @RequestMapping(value = "/relations", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {

9
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java

@ -20,6 +20,7 @@ public class EntityRelationInfo extends EntityRelation {
private static final long serialVersionUID = 2807343097519543363L; private static final long serialVersionUID = 2807343097519543363L;
private String fromName;
private String toName; private String toName;
public EntityRelationInfo() { public EntityRelationInfo() {
@ -30,6 +31,14 @@ public class EntityRelationInfo extends EntityRelation {
super(entityRelation); super(entityRelation);
} }
public String getFromName() {
return fromName;
}
public void setFromName(String fromName) {
this.fromName = fromName;
}
public String getToName() { public String getToName() {
return toName; return toName;
} }

65
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -23,29 +23,17 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.tenant.TenantService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
/** /**
* Created by ashvayka on 28.04.17. * Created by ashvayka on 28.04.17.
@ -133,23 +121,16 @@ public class BaseRelationService implements RelationService {
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation))); relations1.stream().forEach(relation ->
return Futures.successfulAsList(futures); futures.add(fetchRelationInfoAsync(relation,
relation2 -> relation2.getTo(),
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName)))
);
return Futures.successfulAsList(futures);
}); });
return relationsInfo; return relationsInfo;
} }
private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation) {
ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(relation.getTo());
ListenableFuture<EntityRelationInfo> entityRelationInfo =
Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> {
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
entityRelationInfo1.setToName(entityName1);
return entityRelationInfo1;
});
return entityRelationInfo;
}
@Override @Override
public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup);
@ -167,6 +148,38 @@ public class BaseRelationService implements RelationService {
return relationDao.findAllByTo(to, typeGroup); return relationDao.findAllByTo(to, typeGroup);
} }
@Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup) {
log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup);
validate(to);
validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup);
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation ->
futures.add(fetchRelationInfoAsync(relation,
relation2 -> relation2.getFrom(),
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName)))
);
return Futures.successfulAsList(futures);
});
return relationsInfo;
}
private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation,
Function<EntityRelation, EntityId> entityIdGetter,
BiConsumer<EntityRelationInfo, String> entityNameSetter) {
ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation));
ListenableFuture<EntityRelationInfo> entityRelationInfo =
Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> {
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
entityNameSetter.accept(entityRelationInfo1, entityName1);
return entityRelationInfo1;
});
return entityRelationInfo;
}
@Override @Override
public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup);

2
dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java

@ -46,6 +46,8 @@ public interface RelationService {
ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query);

13
ui/src/app/api/entity-relation.service.js

@ -28,6 +28,7 @@ function EntityRelationService($http, $q) {
findInfoByFrom: findInfoByFrom, findInfoByFrom: findInfoByFrom,
findByFromAndType: findByFromAndType, findByFromAndType: findByFromAndType,
findByTo: findByTo, findByTo: findByTo,
findInfoByTo: findInfoByTo,
findByToAndType: findByToAndType, findByToAndType: findByToAndType,
findByQuery: findByQuery findByQuery: findByQuery
} }
@ -122,6 +123,18 @@ function EntityRelationService($http, $q) {
return deferred.promise; return deferred.promise;
} }
function findInfoByTo(toId, toType) {
var deferred = $q.defer();
var url = '/api/relations/info?toId=' + toId;
url += '&toType=' + toType;
$http.get(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function findByToAndType(toId, toType, relationType) { function findByToAndType(toId, toType, relationType) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/relations?toId=' + toId; var url = '/api/relations?toId=' + toId;

6
ui/src/app/customer/customers.tpl.html

@ -55,5 +55,11 @@
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.customer}}">
</tb-relation-table>
</md-tab>
</md-tabs> </md-tabs>
</tb-grid> </tb-grid>

6
ui/src/app/device/devices.tpl.html

@ -56,4 +56,10 @@
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.device}}">
</tb-relation-table>
</md-tab>
</tb-grid> </tb-grid>

2
ui/src/app/entity/entity-filter.tpl.html

@ -41,7 +41,7 @@
</md-autocomplete> </md-autocomplete>
<md-chip-template> <md-chip-template>
<span> <span>
<strong>{{itemName($chip)}}</strong> <strong>{{$chip.name}}</strong>
</span> </span>
</md-chip-template> </md-chip-template>
</md-chips> </md-chips>

3
ui/src/app/entity/entity-select.tpl.html

@ -17,6 +17,9 @@
--> -->
<div layout='row' class="tb-entity-select"> <div layout='row' class="tb-entity-select">
<tb-entity-type-select style="min-width: 100px;" <tb-entity-type-select style="min-width: 100px;"
the-form="theForm"
ng-disabled="disabled"
tb-required="tbRequired"
ng-model="model.entityType"> ng-model="model.entityType">
</tb-entity-type-select> </tb-entity-type-select>
<tb-entity-autocomplete flex ng-if="model.entityType" <tb-entity-autocomplete flex ng-if="model.entityType"

5
ui/src/app/entity/entity-type-select.directive.js

@ -29,6 +29,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
var template = $templateCache.get(entityTypeSelectTemplate); var template = $templateCache.get(entityTypeSelectTemplate);
element.html(template); element.html(template);
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
if (angular.isDefined(attrs.hideLabel)) { if (angular.isDefined(attrs.hideLabel)) {
scope.showLabel = false; scope.showLabel = false;
} else { } else {
@ -103,6 +105,9 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
require: "^ngModel", require: "^ngModel",
link: linker, link: linker,
scope: { scope: {
theForm: '=?',
tbRequired: '=?',
disabled:'=ngDisabled',
allowedEntityTypes: "=?" allowedEntityTypes: "=?"
} }
}; };

8
ui/src/app/entity/entity-type-select.tpl.html

@ -17,9 +17,13 @@
--> -->
<md-input-container> <md-input-container>
<label ng-if="showLabel">{{ 'entity.type' | translate }}</label> <label ng-if="showLabel">{{ 'entity.type' | translate }}</label>
<md-select ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}"> <md-select ng-required="tbRequired" ng-disabled="disabled" name="entityType"
ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}">
<md-option ng-repeat="type in entityTypes" ng-value="type"> <md-option ng-repeat="type in entityTypes" ng-value="type">
{{typeName(type) | translate}} {{typeName(type) | translate}}
</md-option> </md-option>
</md-select> </md-select>
</md-input-container> <div ng-messages="theForm.entityType.$error">
<div ng-message="required" translate>entity.type-required</div>
</div>
</md-input-container>

2
ui/src/app/entity/index.js

@ -27,6 +27,7 @@ import AddAttributeDialogController from './attribute/add-attribute-dialog.contr
import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller';
import AttributeTableDirective from './attribute/attribute-table.directive'; import AttributeTableDirective from './attribute/attribute-table.directive';
import RelationTableDirective from './relation/relation-table.directive'; import RelationTableDirective from './relation/relation-table.directive';
import RelationTypeAutocompleteDirective from './relation/relation-type-autocomplete.directive';
export default angular.module('thingsboard.entity', []) export default angular.module('thingsboard.entity', [])
.controller('EntityAliasesController', EntityAliasesController) .controller('EntityAliasesController', EntityAliasesController)
@ -42,4 +43,5 @@ export default angular.module('thingsboard.entity', [])
.directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
.directive('tbAttributeTable', AttributeTableDirective) .directive('tbAttributeTable', AttributeTableDirective)
.directive('tbRelationTable', RelationTableDirective) .directive('tbRelationTable', RelationTableDirective)
.directive('tbRelationTypeAutocomplete', RelationTypeAutocompleteDirective)
.name; .name;

15
ui/src/app/entity/relation/add-relation-dialog.controller.js

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
/*@ngInject*/ /*@ngInject*/
export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) { export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, direction, entityId) {
var vm = this; var vm = this;
vm.types = types; vm.types = types;
vm.direction = direction;
vm.targetEntityId = {};
vm.relation = {}; vm.relation = {};
vm.relation.from = from; if (vm.direction == vm.types.entitySearchDirection.from) {
vm.relation.from = entityId;
} else {
vm.relation.to = entityId;
}
vm.relation.type = types.entityRelationType.contains; vm.relation.type = types.entityRelationType.contains;
vm.add = add; vm.add = add;
@ -32,6 +38,11 @@ export default function AddRelationDialogController($scope, $mdDialog, types, en
} }
function add() { function add() {
if (vm.direction == vm.types.entitySearchDirection.from) {
vm.relation.to = vm.targetEntityId;
} else {
vm.relation.from = vm.targetEntityId;
}
$scope.theForm.$setPristine(); $scope.theForm.$setPristine();
entityRelationService.saveRelation(vm.relation).then( entityRelationService.saveRelation(vm.relation).then(
function success() { function success() {

17
ui/src/app/entity/relation/add-relation-dialog.tpl.html

@ -32,19 +32,16 @@
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="loading">
<md-input-container class="md-block"> <tb-relation-type-autocomplete ng-model="vm.relation.type"
<label translate>relation.relation-type</label> tb-required="true"
<md-select required ng-model="vm.relation.type" ng-disabled="loading"> ng-disabled="loading">
<md-option ng-repeat="type in vm.types.entityRelationType" ng-value="type"> </tb-relation-type-autocomplete>
<span>{{('relation.relation-types.' + type) | translate}}</span> <small>{{(vm.direction == vm.types.entitySearchDirection.from ?
</md-option> 'relation.to-entity' : 'relation.from-entity') | translate}}</small>
</md-select>
</md-input-container>
<span class="tb-small">{{'entity.entity' | translate }}</span>
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="true" tb-required="true"
ng-model="vm.relation.to"> ng-model="vm.targetEntityId">
</tb-entity-select> </tb-entity-select>
</fieldset> </fieldset>
</md-content> </md-content>

104
ui/src/app/entity/relation/relation-table.directive.js

@ -45,13 +45,17 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
let vm = this; let vm = this;
vm.types = types;
vm.direction = vm.types.entitySearchDirection.from;
vm.relations = []; vm.relations = [];
vm.relationsCount = 0; vm.relationsCount = 0;
vm.allRelations = []; vm.allRelations = [];
vm.selectedRelations = []; vm.selectedRelations = [];
vm.query = { vm.query = {
order: 'typeName', order: 'type',
limit: 5, limit: 5,
page: 1, page: 1,
search: null search: null
@ -62,19 +66,23 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
vm.onReorder = onReorder; vm.onReorder = onReorder;
vm.onPaginate = onPaginate; vm.onPaginate = onPaginate;
vm.addRelation = addRelation; vm.addRelation = addRelation;
vm.editRelation = editRelation;
vm.deleteRelation = deleteRelation; vm.deleteRelation = deleteRelation;
vm.deleteRelations = deleteRelations; vm.deleteRelations = deleteRelations;
vm.reloadRelations = reloadRelations; vm.reloadRelations = reloadRelations;
vm.updateRelations = updateRelations; vm.updateRelations = updateRelations;
$scope.$watch("vm.entityId", function(newVal, prevVal) { $scope.$watch("vm.entityId", function(newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) { if (newVal && !angular.equals(newVal, prevVal)) {
reloadRelations(); reloadRelations();
} }
}); });
$scope.$watch("vm.direction", function(newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) {
reloadRelations();
}
});
$scope.$watch("vm.query.search", function(newVal, prevVal) { $scope.$watch("vm.query.search", function(newVal, prevVal) {
if (!angular.equals(newVal, prevVal) && vm.query.search != null) { if (!angular.equals(newVal, prevVal) && vm.query.search != null) {
updateRelations(); updateRelations();
@ -102,7 +110,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
var from = { var entityId = {
id: vm.entityId, id: vm.entityId,
entityType: vm.entityType entityType: vm.entityType
}; };
@ -111,7 +119,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: addRelationTemplate, templateUrl: addRelationTemplate,
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
locals: { from: from }, locals: { direction: vm.direction, entityId: entityId },
fullscreen: true, fullscreen: true,
targetEvent: $event targetEvent: $event
}).then(function () { }).then(function () {
@ -120,36 +128,100 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
}); });
} }
function editRelation($event, /*relation*/) { function deleteRelation($event, relation) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
//TODO: if (relation) {
} var title;
var content;
if (vm.direction == vm.types.entitySearchDirection.from) {
title = $translate.instant('relation.delete-to-relation-title', {entityName: relation.toName});
content = $translate.instant('relation.delete-to-relation-text', {entityName: relation.toName});
} else {
title = $translate.instant('relation.delete-from-relation-title', {entityName: relation.fromName});
content = $translate.instant('relation.delete-from-relation-text', {entityName: relation.fromName});
}
function deleteRelation($event, /*relation*/) { var confirm = $mdDialog.confirm()
if ($event) { .targetEvent($event)
$event.stopPropagation(); .title(title)
.htmlContent(content)
.ariaLabel(title)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
entityRelationService.deleteRelation(
relation.from.id,
relation.from.entityType,
relation.type,
relation.to.id,
relation.to.entityType).then(
function success() {
reloadRelations();
}
);
});
} }
//TODO:
} }
function deleteRelations($event) { function deleteRelations($event) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
//TODO: if (vm.selectedRelations && vm.selectedRelations.length > 0) {
var title;
var content;
if (vm.direction == vm.types.entitySearchDirection.from) {
title = $translate.instant('relation.delete-to-relations-title', {count: vm.selectedRelations.length}, 'messageformat');
content = $translate.instant('relation.delete-to-relations-text');
} else {
title = $translate.instant('relation.delete-from-relations-title', {count: vm.selectedRelations.length}, 'messageformat');
content = $translate.instant('relation.delete-from-relations-text');
}
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title(title)
.htmlContent(content)
.ariaLabel(title)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
var tasks = [];
for (var i=0;i<vm.selectedRelations.length;i++) {
var relation = vm.selectedRelations[i];
tasks.push( entityRelationService.deleteRelation(
relation.from.id,
relation.from.entityType,
relation.type,
relation.to.id,
relation.to.entityType));
}
$q.all(tasks).then(function () {
reloadRelations();
});
});
}
} }
function reloadRelations () { function reloadRelations () {
vm.allRelations.length = 0; vm.allRelations.length = 0;
vm.relations.length = 0; vm.relations.length = 0;
vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); vm.relationsPromise;
if (vm.direction == vm.types.entitySearchDirection.from) {
vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType);
} else {
vm.relationsPromise = entityRelationService.findInfoByTo(vm.entityId, vm.entityType);
}
vm.relationsPromise.then( vm.relationsPromise.then(
function success(allRelations) { function success(allRelations) {
allRelations.forEach(function(relation) { allRelations.forEach(function(relation) {
relation.typeName = $translate.instant('relation.relation-type.' + relation.type); if (vm.direction == vm.types.entitySearchDirection.from) {
relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType));
} else {
relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType));
}
}); });
vm.allRelations = allRelations; vm.allRelations = allRelations;
vm.selectedRelations = []; vm.selectedRelations = [];

42
ui/src/app/entity/relation/relation-table.tpl.html

@ -16,11 +16,22 @@
--> -->
<md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column"> <md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column">
<section layout="row">
<md-input-container class="md-block" style="width: 200px;">
<label translate>relation.direction</label>
<md-select ng-model="vm.direction" ng-disabled="loading">
<md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction">
{{ ('relation.search-direction.' + direction) | translate}}
</md-option>
</md-select>
</md-input-container>
</section>
<div layout="column" class="md-whiteframe-z1"> <div layout="column" class="md-whiteframe-z1">
<md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
&& vm.query.search === null"> && vm.query.search === null">
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<span translate>relation.entity-relations</span> <span>{{(vm.direction == vm.types.entitySearchDirection.from ?
'relation.from-relations' : 'relation.to-relations') | translate}}</span>
<span flex></span> <span flex></span>
<md-button class="md-icon-button" ng-click="vm.addRelation($event)"> <md-button class="md-icon-button" ng-click="vm.addRelation($event)">
<md-icon>add</md-icon> <md-icon>add</md-icon>
@ -66,7 +77,7 @@
<md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length"> <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length">
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<span translate <span translate
translate-values="{count: selectedRelations.length}" translate-values="{count: vm.selectedRelations.length}"
translate-interpolation="messageformat">relation.selected-relations</span> translate-interpolation="messageformat">relation.selected-relations</span>
<span flex></span> <span flex></span>
<md-button class="md-icon-button" ng-click="vm.deleteRelations($event)"> <md-button class="md-icon-button" ng-click="vm.deleteRelations($event)">
@ -81,25 +92,26 @@
<table md-table md-row-select multiple="" ng-model="vm.selectedRelations" md-progress="vm.relationsDeferred.promise"> <table md-table md-row-select multiple="" ng-model="vm.selectedRelations" md-progress="vm.relationsDeferred.promise">
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
<tr md-row> <tr md-row>
<th md-column md-order-by="typeName"><span translate>relation.type</span></th> <th md-column md-order-by="type"><span translate>relation.type</span></th>
<th md-column md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th> <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from"
<th md-column md-order-by="toName"><span translate>relation.to-entity-name</span></th> md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th>
<th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to"
md-order-by="fromEntityTypeName"><span translate>relation.from-entity-type</span></th>
<th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from"
md-order-by="toName"><span translate>relation.to-entity-name</span></th>
<th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to"
md-order-by="fromName"><span translate>relation.from-entity-name</span></th>
<th md-column><span>&nbsp</span></th> <th md-column><span>&nbsp</span></th>
</tr> </tr>
</thead> </thead>
<tbody md-body> <tbody md-body>
<tr md-row md-select="relation" md-select-id="relation" md-auto-select ng-repeat="relation in vm.relations"> <tr md-row md-select="relation" md-select-id="relation" md-auto-select ng-repeat="relation in vm.relations">
<td md-cell>{{ relation.typeName }}</td> <td md-cell>{{ relation.type }}</td>
<td md-cell>{{ relation.toEntityTypeName }}</td> <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toEntityTypeName }}</td>
<td md-cell>{{ relation.toName }}</td> <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromEntityTypeName }}</td>
<td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toName }}</td>
<td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromName }}</td>
<td md-cell class="tb-action-cell"> <td md-cell class="tb-action-cell">
<md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}"
ng-click="vm.editRelation($event, relation)">
<md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon>
<md-tooltip md-direction="top">
{{ 'relation.edit' | translate }}
</md-tooltip>
</md-button>
<md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)"> <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)">
<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon> <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon>
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">

86
ui/src/app/entity/relation/relation-type-autocomplete.directive.js

@ -0,0 +1,86 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import './relation-type-autocomplete.scss';
/* eslint-disable import/no-unresolved, import/default */
import relationTypeAutocompleteTemplate from './relation-type-autocomplete.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function RelationTypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, types) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(relationTypeAutocompleteTemplate);
element.html(template);
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
scope.relationType = null;
scope.relationTypeSearchText = '';
scope.relationTypes = [];
for (var type in types.entityRelationType) {
scope.relationTypes.push(types.entityRelationType[type]);
}
scope.fetchRelationTypes = function(searchText) {
var deferred = $q.defer();
var result = $filter('filter')(scope.relationTypes, {'$': searchText});
if (result && result.length) {
deferred.resolve(result);
} else {
deferred.resolve([searchText]);
}
return deferred.promise;
}
scope.relationTypeSearchTextChanged = function() {
}
scope.updateView = function () {
if (!scope.disabled) {
ngModelCtrl.$setViewValue(scope.relationType);
}
}
ngModelCtrl.$render = function () {
scope.relationType = ngModelCtrl.$viewValue;
}
scope.$watch('relationType', function (newValue, prevValue) {
if (!angular.equals(newValue, prevValue)) {
scope.updateView();
}
});
scope.$watch('disabled', function () {
scope.updateView();
});
$compile(element.contents())(scope);
}
return {
restrict: "E",
require: "^ngModel",
link: linker,
scope: {
theForm: '=?',
tbRequired: '=?',
disabled:'=ngDisabled'
}
};
}

25
ui/src/app/entity/relation/relation-type-autocomplete.scss

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-relation-type-autocomplete {
.tb-relation-type-item {
display: block;
height: 48px;
}
li {
height: auto !important;
white-space: normal !important;
}
}

40
ui/src/app/entity/relation/relation-type-autocomplete.tpl.html

@ -0,0 +1,40 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-autocomplete ng-required="tbRequired"
ng-disabled="disabled"
md-no-cache="true"
md-input-name="relationType"
ng-model="relationType"
md-selected-item="relationType"
md-search-text="relationTypeSearchText"
md-search-text-change="relationTypeSearchTextChanged()"
md-items="item in fetchRelationTypes(relationTypeSearchText)"
md-item-text="item"
md-min-length="0"
md-floating-label="{{ 'relation.relation-type' | translate }}"
md-select-on-match="true"
md-menu-class="tb-relation-type-autocomplete">
<md-item-template>
<div class="tb-relation-type-item">
<span md-highlight-text="relationTypeSearchText" md-highlight-flags="^i">{{item}}</span>
</div>
</md-item-template>
<div ng-messages="theForm.relationType.$error">
<div translate ng-message="required">relation.relation-type-required</div>
</div>
</md-autocomplete>

33
ui/src/app/locale/locale.constant.js

@ -544,6 +544,7 @@ export default angular.module('thingsboard.locale', [])
"entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
"all-subtypes": "All", "all-subtypes": "All",
"type": "Type", "type": "Type",
"type-required": "Entity type is required.",
"type-device": "Device", "type-device": "Device",
"type-asset": "Asset", "type-asset": "Asset",
"type-rule": "Rule", "type-rule": "Rule",
@ -718,19 +719,33 @@ export default angular.module('thingsboard.locale', [])
}, },
"relation": { "relation": {
"relations": "Relations", "relations": "Relations",
"entity-relations": "Entity relations", "direction": "Direction",
"search-direction": {
"FROM": "From",
"TO": "To"
},
"from-relations": "Outbound relations",
"to-relations": "Inbound relations",
"selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected", "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
"type": "Type", "type": "Type",
"to-entity-type": "Entity type", "to-entity-type": "To entity type",
"to-entity-name": "Entity name", "to-entity-name": "To entity name",
"edit": "Edit relation", "from-entity-type": "From entity type",
"from-entity-name": "From entity name",
"to-entity": "To entity",
"from-entity": "From entity",
"delete": "Delete relation", "delete": "Delete relation",
"relation-type": "Relation type", "relation-type": "Relation type",
"relation-types": { "relation-type-required": "Relation type is required.",
"Contains": "Contains", "add": "Add relation",
"Manages": "Manages" "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
}, "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
"add": "Add relation" "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
"delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
"delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
"delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
"delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
"delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities."
}, },
"rule": { "rule": {
"rule": "Rule", "rule": "Rule",

6
ui/src/app/plugin/plugins.tpl.html

@ -56,5 +56,11 @@
disabled-event-types="{{vm.types.eventType.alarm.value}}"> disabled-event-types="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.plugin}}">
</tb-relation-table>
</md-tab>
</md-tabs> </md-tabs>
</tb-grid> </tb-grid>

6
ui/src/app/rule/rules.tpl.html

@ -56,5 +56,11 @@
disabled-event-types="{{vm.types.eventType.alarm.value}}"> disabled-event-types="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.rule}}">
</tb-relation-table>
</md-tab>
</md-tabs> </md-tabs>
</tb-grid> </tb-grid>

6
ui/src/app/tenant/tenants.tpl.html

@ -53,5 +53,11 @@
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.tenant}}">
</tb-relation-table>
</md-tab>
</md-tabs> </md-tabs>
</tb-grid> </tb-grid>

1
ui/src/scss/main.scss

@ -436,6 +436,7 @@ md-tabs.tb-headless {
***********************/ ***********************/
section.tb-header-buttons { section.tb-header-buttons {
pointer-events: none;
position: absolute; position: absolute;
right: 0px; right: 0px;
top: 86px; top: 86px;

Loading…
Cancel
Save