Merge remote-tracking branch 'upstream/master' into dao-refactoring-vs
This commit is contained in:
commit
2f79950178
@ -34,7 +34,7 @@ import java.util.List;
|
||||
@RequestMapping("/api")
|
||||
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)
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
|
||||
@ -42,31 +42,33 @@ public class EntityRelationController extends BaseController {
|
||||
checkNotNull(relation);
|
||||
checkEntityId(relation.getFrom());
|
||||
checkEntityId(relation.getTo());
|
||||
if (relation.getTypeGroup() == null) {
|
||||
relation.setTypeGroup(RelationTypeGroup.COMMON);
|
||||
}
|
||||
relationService.saveRelation(relation).get();
|
||||
} catch (Exception 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"})
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
public void deleteRelation(@RequestParam("fromId") String strFromId,
|
||||
@RequestParam("fromType") String strFromType,
|
||||
@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 {
|
||||
checkParameter("fromId", strFromId);
|
||||
checkParameter("fromType", strFromType);
|
||||
checkParameter("relationType", strRelationType);
|
||||
checkParameter("relationTypeGroup", strRelationTypeGroup);
|
||||
checkParameter("toId", strToId);
|
||||
checkParameter("toType", strToType);
|
||||
EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
|
||||
EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
|
||||
checkEntityId(fromId);
|
||||
checkEntityId(toId);
|
||||
RelationTypeGroup relationTypeGroup = RelationTypeGroup.valueOf(strRelationTypeGroup);
|
||||
RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
|
||||
try {
|
||||
Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get();
|
||||
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"})
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
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"})
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
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"})
|
||||
@ResponseBody
|
||||
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"})
|
||||
@ResponseBody
|
||||
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"})
|
||||
@ResponseBody
|
||||
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"})
|
||||
@ResponseBody
|
||||
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"})
|
||||
@ResponseBody
|
||||
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)
|
||||
@ResponseBody
|
||||
public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {
|
||||
|
||||
@ -20,6 +20,7 @@ public class EntityRelationInfo extends EntityRelation {
|
||||
|
||||
private static final long serialVersionUID = 2807343097519543363L;
|
||||
|
||||
private String fromName;
|
||||
private String toName;
|
||||
|
||||
public EntityRelationInfo() {
|
||||
@ -30,6 +31,14 @@ public class EntityRelationInfo extends EntityRelation {
|
||||
super(entityRelation);
|
||||
}
|
||||
|
||||
public String getFromName() {
|
||||
return fromName;
|
||||
}
|
||||
|
||||
public void setFromName(String fromName) {
|
||||
this.fromName = fromName;
|
||||
}
|
||||
|
||||
public String getToName() {
|
||||
return toName;
|
||||
}
|
||||
|
||||
@ -23,29 +23,17 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
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.UUIDBased;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
||||
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.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 java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 28.04.17.
|
||||
@ -133,23 +121,16 @@ public class BaseRelationService implements RelationService {
|
||||
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)));
|
||||
relations1.stream().forEach(relation ->
|
||||
futures.add(fetchRelationInfoAsync(relation,
|
||||
relation2 -> relation2.getTo(),
|
||||
(EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName)))
|
||||
);
|
||||
return Futures.successfulAsList(futures);
|
||||
});
|
||||
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
|
||||
public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) {
|
||||
log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup);
|
||||
@ -167,6 +148,38 @@ public class BaseRelationService implements RelationService {
|
||||
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
|
||||
public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) {
|
||||
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup);
|
||||
|
||||
@ -46,6 +46,8 @@ public interface RelationService {
|
||||
|
||||
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>> findByQuery(EntityRelationsQuery query);
|
||||
|
||||
@ -28,6 +28,7 @@ function EntityRelationService($http, $q) {
|
||||
findInfoByFrom: findInfoByFrom,
|
||||
findByFromAndType: findByFromAndType,
|
||||
findByTo: findByTo,
|
||||
findInfoByTo: findInfoByTo,
|
||||
findByToAndType: findByToAndType,
|
||||
findByQuery: findByQuery
|
||||
}
|
||||
@ -122,6 +123,18 @@ function EntityRelationService($http, $q) {
|
||||
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) {
|
||||
var deferred = $q.defer();
|
||||
var url = '/api/relations?toId=' + toId;
|
||||
|
||||
@ -55,5 +55,11 @@
|
||||
default-event-type="{{vm.types.eventType.alarm.value}}">
|
||||
</tb-event-table>
|
||||
</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>
|
||||
</tb-grid>
|
||||
|
||||
@ -56,4 +56,10 @@
|
||||
default-event-type="{{vm.types.eventType.alarm.value}}">
|
||||
</tb-event-table>
|
||||
</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>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
</md-autocomplete>
|
||||
<md-chip-template>
|
||||
<span>
|
||||
<strong>{{itemName($chip)}}</strong>
|
||||
<strong>{{$chip.name}}</strong>
|
||||
</span>
|
||||
</md-chip-template>
|
||||
</md-chips>
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
-->
|
||||
<div layout='row' class="tb-entity-select">
|
||||
<tb-entity-type-select style="min-width: 100px;"
|
||||
the-form="theForm"
|
||||
ng-disabled="disabled"
|
||||
tb-required="tbRequired"
|
||||
ng-model="model.entityType">
|
||||
</tb-entity-type-select>
|
||||
<tb-entity-autocomplete flex ng-if="model.entityType"
|
||||
|
||||
@ -29,6 +29,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
|
||||
var template = $templateCache.get(entityTypeSelectTemplate);
|
||||
element.html(template);
|
||||
|
||||
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
|
||||
|
||||
if (angular.isDefined(attrs.hideLabel)) {
|
||||
scope.showLabel = false;
|
||||
} else {
|
||||
@ -103,6 +105,9 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
|
||||
require: "^ngModel",
|
||||
link: linker,
|
||||
scope: {
|
||||
theForm: '=?',
|
||||
tbRequired: '=?',
|
||||
disabled:'=ngDisabled',
|
||||
allowedEntityTypes: "=?"
|
||||
}
|
||||
};
|
||||
|
||||
@ -17,9 +17,13 @@
|
||||
-->
|
||||
<md-input-container>
|
||||
<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">
|
||||
{{typeName(type) | translate}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
<div ng-messages="theForm.entityType.$error">
|
||||
<div ng-message="required" translate>entity.type-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
@ -27,6 +27,7 @@ import AddAttributeDialogController from './attribute/add-attribute-dialog.contr
|
||||
import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller';
|
||||
import AttributeTableDirective from './attribute/attribute-table.directive';
|
||||
import RelationTableDirective from './relation/relation-table.directive';
|
||||
import RelationTypeAutocompleteDirective from './relation/relation-type-autocomplete.directive';
|
||||
|
||||
export default angular.module('thingsboard.entity', [])
|
||||
.controller('EntityAliasesController', EntityAliasesController)
|
||||
@ -42,4 +43,5 @@ export default angular.module('thingsboard.entity', [])
|
||||
.directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
|
||||
.directive('tbAttributeTable', AttributeTableDirective)
|
||||
.directive('tbRelationTable', RelationTableDirective)
|
||||
.directive('tbRelationTypeAutocomplete', RelationTypeAutocompleteDirective)
|
||||
.name;
|
||||
|
||||
@ -14,14 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*@ngInject*/
|
||||
export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) {
|
||||
export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, direction, entityId) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.types = types;
|
||||
vm.direction = direction;
|
||||
vm.targetEntityId = {};
|
||||
|
||||
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.add = add;
|
||||
@ -32,6 +38,11 @@ export default function AddRelationDialogController($scope, $mdDialog, types, en
|
||||
}
|
||||
|
||||
function add() {
|
||||
if (vm.direction == vm.types.entitySearchDirection.from) {
|
||||
vm.relation.to = vm.targetEntityId;
|
||||
} else {
|
||||
vm.relation.from = vm.targetEntityId;
|
||||
}
|
||||
$scope.theForm.$setPristine();
|
||||
entityRelationService.saveRelation(vm.relation).then(
|
||||
function success() {
|
||||
|
||||
@ -32,19 +32,16 @@
|
||||
<div class="md-dialog-content">
|
||||
<md-content class="md-padding" layout="column">
|
||||
<fieldset ng-disabled="loading">
|
||||
<md-input-container class="md-block">
|
||||
<label translate>relation.relation-type</label>
|
||||
<md-select required ng-model="vm.relation.type" ng-disabled="loading">
|
||||
<md-option ng-repeat="type in vm.types.entityRelationType" ng-value="type">
|
||||
<span>{{('relation.relation-types.' + type) | translate}}</span>
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<span class="tb-small">{{'entity.entity' | translate }}</span>
|
||||
<tb-relation-type-autocomplete ng-model="vm.relation.type"
|
||||
tb-required="true"
|
||||
ng-disabled="loading">
|
||||
</tb-relation-type-autocomplete>
|
||||
<small>{{(vm.direction == vm.types.entitySearchDirection.from ?
|
||||
'relation.to-entity' : 'relation.from-entity') | translate}}</small>
|
||||
<tb-entity-select flex
|
||||
the-form="theForm"
|
||||
tb-required="true"
|
||||
ng-model="vm.relation.to">
|
||||
ng-model="vm.targetEntityId">
|
||||
</tb-entity-select>
|
||||
</fieldset>
|
||||
</md-content>
|
||||
|
||||
@ -45,13 +45,17 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
|
||||
|
||||
let vm = this;
|
||||
|
||||
vm.types = types;
|
||||
|
||||
vm.direction = vm.types.entitySearchDirection.from;
|
||||
|
||||
vm.relations = [];
|
||||
vm.relationsCount = 0;
|
||||
vm.allRelations = [];
|
||||
vm.selectedRelations = [];
|
||||
|
||||
vm.query = {
|
||||
order: 'typeName',
|
||||
order: 'type',
|
||||
limit: 5,
|
||||
page: 1,
|
||||
search: null
|
||||
@ -62,19 +66,23 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
|
||||
vm.onReorder = onReorder;
|
||||
vm.onPaginate = onPaginate;
|
||||
vm.addRelation = addRelation;
|
||||
vm.editRelation = editRelation;
|
||||
vm.deleteRelation = deleteRelation;
|
||||
vm.deleteRelations = deleteRelations;
|
||||
vm.reloadRelations = reloadRelations;
|
||||
vm.updateRelations = updateRelations;
|
||||
|
||||
|
||||
$scope.$watch("vm.entityId", function(newVal, prevVal) {
|
||||
if (newVal && !angular.equals(newVal, prevVal)) {
|
||||
reloadRelations();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch("vm.direction", function(newVal, prevVal) {
|
||||
if (newVal && !angular.equals(newVal, prevVal)) {
|
||||
reloadRelations();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch("vm.query.search", function(newVal, prevVal) {
|
||||
if (!angular.equals(newVal, prevVal) && vm.query.search != null) {
|
||||
updateRelations();
|
||||
@ -102,7 +110,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
var from = {
|
||||
var entityId = {
|
||||
id: vm.entityId,
|
||||
entityType: vm.entityType
|
||||
};
|
||||
@ -111,7 +119,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
|
||||
controllerAs: 'vm',
|
||||
templateUrl: addRelationTemplate,
|
||||
parent: angular.element($document[0].body),
|
||||
locals: { from: from },
|
||||
locals: { direction: vm.direction, entityId: entityId },
|
||||
fullscreen: true,
|
||||
targetEvent: $event
|
||||
}).then(function () {
|
||||
@ -120,36 +128,100 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
|
||||
});
|
||||
}
|
||||
|
||||
function editRelation($event, /*relation*/) {
|
||||
function deleteRelation($event, relation) {
|
||||
if ($event) {
|
||||
$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*/) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
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 () {
|
||||
entityRelationService.deleteRelation(
|
||||
relation.from.id,
|
||||
relation.from.entityType,
|
||||
relation.type,
|
||||
relation.to.id,
|
||||
relation.to.entityType).then(
|
||||
function success() {
|
||||
reloadRelations();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
//TODO:
|
||||
}
|
||||
|
||||
function deleteRelations($event) {
|
||||
if ($event) {
|
||||
$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 () {
|
||||
vm.allRelations.length = 0;
|
||||
vm.relations.length = 0;
|
||||
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(
|
||||
function success(allRelations) {
|
||||
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));
|
||||
} else {
|
||||
relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType));
|
||||
}
|
||||
});
|
||||
vm.allRelations = allRelations;
|
||||
vm.selectedRelations = [];
|
||||
|
||||
@ -16,11 +16,22 @@
|
||||
|
||||
-->
|
||||
<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">
|
||||
<md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
|
||||
&& vm.query.search === null">
|
||||
<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>
|
||||
<md-button class="md-icon-button" ng-click="vm.addRelation($event)">
|
||||
<md-icon>add</md-icon>
|
||||
@ -66,7 +77,7 @@
|
||||
<md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length">
|
||||
<div class="md-toolbar-tools">
|
||||
<span translate
|
||||
translate-values="{count: selectedRelations.length}"
|
||||
translate-values="{count: vm.selectedRelations.length}"
|
||||
translate-interpolation="messageformat">relation.selected-relations</span>
|
||||
<span flex></span>
|
||||
<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">
|
||||
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
|
||||
<tr md-row>
|
||||
<th md-column md-order-by="typeName"><span translate>relation.type</span></th>
|
||||
<th md-column md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th>
|
||||
<th md-column md-order-by="toName"><span translate>relation.to-entity-name</span></th>
|
||||
<th md-column md-order-by="type"><span translate>relation.type</span></th>
|
||||
<th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from"
|
||||
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> </span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<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.toEntityTypeName }}</td>
|
||||
<td md-cell>{{ relation.toName }}</td>
|
||||
<td md-cell>{{ relation.type }}</td>
|
||||
<td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toEntityTypeName }}</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">
|
||||
<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-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
|
||||
@ -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
Normal file
25
ui/src/app/entity/relation/relation-type-autocomplete.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -544,6 +544,7 @@ export default angular.module('thingsboard.locale', [])
|
||||
"entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
|
||||
"all-subtypes": "All",
|
||||
"type": "Type",
|
||||
"type-required": "Entity type is required.",
|
||||
"type-device": "Device",
|
||||
"type-asset": "Asset",
|
||||
"type-rule": "Rule",
|
||||
@ -718,19 +719,33 @@ export default angular.module('thingsboard.locale', [])
|
||||
},
|
||||
"relation": {
|
||||
"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",
|
||||
"type": "Type",
|
||||
"to-entity-type": "Entity type",
|
||||
"to-entity-name": "Entity name",
|
||||
"edit": "Edit relation",
|
||||
"to-entity-type": "To entity type",
|
||||
"to-entity-name": "To entity name",
|
||||
"from-entity-type": "From entity type",
|
||||
"from-entity-name": "From entity name",
|
||||
"to-entity": "To entity",
|
||||
"from-entity": "From entity",
|
||||
"delete": "Delete relation",
|
||||
"relation-type": "Relation type",
|
||||
"relation-types": {
|
||||
"Contains": "Contains",
|
||||
"Manages": "Manages"
|
||||
},
|
||||
"add": "Add relation"
|
||||
"relation-type-required": "Relation type is required.",
|
||||
"add": "Add relation",
|
||||
"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.",
|
||||
"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",
|
||||
|
||||
@ -56,5 +56,11 @@
|
||||
disabled-event-types="{{vm.types.eventType.alarm.value}}">
|
||||
</tb-event-table>
|
||||
</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>
|
||||
</tb-grid>
|
||||
|
||||
@ -56,5 +56,11 @@
|
||||
disabled-event-types="{{vm.types.eventType.alarm.value}}">
|
||||
</tb-event-table>
|
||||
</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>
|
||||
</tb-grid>
|
||||
|
||||
@ -53,5 +53,11 @@
|
||||
default-event-type="{{vm.types.eventType.alarm.value}}">
|
||||
</tb-event-table>
|
||||
</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>
|
||||
</tb-grid>
|
||||
|
||||
@ -436,6 +436,7 @@ md-tabs.tb-headless {
|
||||
***********************/
|
||||
|
||||
section.tb-header-buttons {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 86px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user