Commit ecb5c50d61add5a92c33ee91afef5244109e8480
1 parent
3c43d039
UI: Implement Entity relations management.
Showing
24 changed files
with
454 additions
and
94 deletions
... | ... | @@ -34,7 +34,7 @@ import java.util.List; |
34 | 34 | @RequestMapping("/api") |
35 | 35 | public class EntityRelationController extends BaseController { |
36 | 36 | |
37 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
37 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
38 | 38 | @RequestMapping(value = "/relation", method = RequestMethod.POST) |
39 | 39 | @ResponseStatus(value = HttpStatus.OK) |
40 | 40 | public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { |
... | ... | @@ -42,31 +42,33 @@ public class EntityRelationController extends BaseController { |
42 | 42 | checkNotNull(relation); |
43 | 43 | checkEntityId(relation.getFrom()); |
44 | 44 | checkEntityId(relation.getTo()); |
45 | + if (relation.getTypeGroup() == null) { | |
46 | + relation.setTypeGroup(RelationTypeGroup.COMMON); | |
47 | + } | |
45 | 48 | relationService.saveRelation(relation).get(); |
46 | 49 | } catch (Exception e) { |
47 | 50 | throw handleException(e); |
48 | 51 | } |
49 | 52 | } |
50 | 53 | |
51 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
54 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
52 | 55 | @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"}) |
53 | 56 | @ResponseStatus(value = HttpStatus.OK) |
54 | 57 | public void deleteRelation(@RequestParam("fromId") String strFromId, |
55 | 58 | @RequestParam("fromType") String strFromType, |
56 | 59 | @RequestParam("relationType") String strRelationType, |
57 | - @RequestParam("relationTypeGroup") String strRelationTypeGroup, | |
60 | + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, | |
58 | 61 | @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { |
59 | 62 | checkParameter("fromId", strFromId); |
60 | 63 | checkParameter("fromType", strFromType); |
61 | 64 | checkParameter("relationType", strRelationType); |
62 | - checkParameter("relationTypeGroup", strRelationTypeGroup); | |
63 | 65 | checkParameter("toId", strToId); |
64 | 66 | checkParameter("toType", strToType); |
65 | 67 | EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); |
66 | 68 | EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); |
67 | 69 | checkEntityId(fromId); |
68 | 70 | checkEntityId(toId); |
69 | - RelationTypeGroup relationTypeGroup = RelationTypeGroup.valueOf(strRelationTypeGroup); | |
71 | + RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); | |
70 | 72 | try { |
71 | 73 | Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get(); |
72 | 74 | if (!found) { |
... | ... | @@ -77,7 +79,7 @@ public class EntityRelationController extends BaseController { |
77 | 79 | } |
78 | 80 | } |
79 | 81 | |
80 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
82 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") | |
81 | 83 | @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"}) |
82 | 84 | @ResponseStatus(value = HttpStatus.OK) |
83 | 85 | public void deleteRelations(@RequestParam("entityId") String strId, |
... | ... | @@ -93,7 +95,7 @@ public class EntityRelationController extends BaseController { |
93 | 95 | } |
94 | 96 | } |
95 | 97 | |
96 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
98 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
97 | 99 | @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"}) |
98 | 100 | @ResponseStatus(value = HttpStatus.OK) |
99 | 101 | public void checkRelation(@RequestParam("fromId") String strFromId, |
... | ... | @@ -121,7 +123,7 @@ public class EntityRelationController extends BaseController { |
121 | 123 | } |
122 | 124 | } |
123 | 125 | |
124 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
126 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
125 | 127 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"}) |
126 | 128 | @ResponseBody |
127 | 129 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, |
... | ... | @@ -139,7 +141,7 @@ public class EntityRelationController extends BaseController { |
139 | 141 | } |
140 | 142 | } |
141 | 143 | |
142 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
144 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
143 | 145 | @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) |
144 | 146 | @ResponseBody |
145 | 147 | public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, |
... | ... | @@ -157,7 +159,7 @@ public class EntityRelationController extends BaseController { |
157 | 159 | } |
158 | 160 | } |
159 | 161 | |
160 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
162 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
161 | 163 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) |
162 | 164 | @ResponseBody |
163 | 165 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, |
... | ... | @@ -177,7 +179,7 @@ public class EntityRelationController extends BaseController { |
177 | 179 | } |
178 | 180 | } |
179 | 181 | |
180 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
182 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
181 | 183 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"}) |
182 | 184 | @ResponseBody |
183 | 185 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, |
... | ... | @@ -195,7 +197,25 @@ public class EntityRelationController extends BaseController { |
195 | 197 | } |
196 | 198 | } |
197 | 199 | |
198 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
200 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
201 | + @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"toId", "toType"}) | |
202 | + @ResponseBody | |
203 | + public List<EntityRelationInfo> findInfoByTo(@RequestParam("toId") String strToId, | |
204 | + @RequestParam("toType") String strToType, | |
205 | + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { | |
206 | + checkParameter("toId", strToId); | |
207 | + checkParameter("toType", strToType); | |
208 | + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); | |
209 | + checkEntityId(entityId); | |
210 | + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); | |
211 | + try { | |
212 | + return checkNotNull(relationService.findInfoByTo(entityId, typeGroup).get()); | |
213 | + } catch (Exception e) { | |
214 | + throw handleException(e); | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
199 | 219 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"}) |
200 | 220 | @ResponseBody |
201 | 221 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, |
... | ... | @@ -215,7 +235,7 @@ public class EntityRelationController extends BaseController { |
215 | 235 | } |
216 | 236 | } |
217 | 237 | |
218 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
238 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
219 | 239 | @RequestMapping(value = "/relations", method = RequestMethod.POST) |
220 | 240 | @ResponseBody |
221 | 241 | public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { | ... | ... |
... | ... | @@ -20,6 +20,7 @@ public class EntityRelationInfo extends EntityRelation { |
20 | 20 | |
21 | 21 | private static final long serialVersionUID = 2807343097519543363L; |
22 | 22 | |
23 | + private String fromName; | |
23 | 24 | private String toName; |
24 | 25 | |
25 | 26 | public EntityRelationInfo() { |
... | ... | @@ -30,6 +31,14 @@ public class EntityRelationInfo extends EntityRelation { |
30 | 31 | super(entityRelation); |
31 | 32 | } |
32 | 33 | |
34 | + public String getFromName() { | |
35 | + return fromName; | |
36 | + } | |
37 | + | |
38 | + public void setFromName(String fromName) { | |
39 | + this.fromName = fromName; | |
40 | + } | |
41 | + | |
33 | 42 | public String getToName() { |
34 | 43 | return toName; |
35 | 44 | } | ... | ... |
... | ... | @@ -23,29 +23,17 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | 25 | import org.springframework.util.StringUtils; |
26 | -import org.thingsboard.server.common.data.BaseData; | |
27 | -import org.thingsboard.server.common.data.Device; | |
28 | -import org.thingsboard.server.common.data.EntityType; | |
29 | -import org.thingsboard.server.common.data.asset.Asset; | |
30 | -import org.thingsboard.server.common.data.id.AssetId; | |
31 | -import org.thingsboard.server.common.data.id.DeviceId; | |
32 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
33 | -import org.thingsboard.server.common.data.id.UUIDBased; | |
34 | 27 | import org.thingsboard.server.common.data.relation.EntityRelation; |
35 | 28 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; |
36 | 29 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
37 | -import org.thingsboard.server.dao.asset.AssetService; | |
38 | -import org.thingsboard.server.dao.customer.CustomerService; | |
39 | -import org.thingsboard.server.dao.device.DeviceService; | |
40 | 30 | import org.thingsboard.server.dao.entity.EntityService; |
41 | 31 | import org.thingsboard.server.dao.exception.DataValidationException; |
42 | -import org.thingsboard.server.dao.plugin.PluginService; | |
43 | -import org.thingsboard.server.dao.rule.RuleService; | |
44 | -import org.thingsboard.server.dao.tenant.TenantService; | |
45 | 32 | |
46 | 33 | import javax.annotation.Nullable; |
47 | 34 | import java.util.*; |
48 | 35 | import java.util.concurrent.ConcurrentHashMap; |
36 | +import java.util.function.BiConsumer; | |
49 | 37 | |
50 | 38 | /** |
51 | 39 | * Created by ashvayka on 28.04.17. |
... | ... | @@ -133,23 +121,16 @@ public class BaseRelationService implements RelationService { |
133 | 121 | ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, |
134 | 122 | (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
135 | 123 | List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
136 | - relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation))); | |
137 | - return Futures.successfulAsList(futures); | |
124 | + relations1.stream().forEach(relation -> | |
125 | + futures.add(fetchRelationInfoAsync(relation, | |
126 | + relation2 -> relation2.getTo(), | |
127 | + (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) | |
128 | + ); | |
129 | + return Futures.successfulAsList(futures); | |
138 | 130 | }); |
139 | 131 | return relationsInfo; |
140 | 132 | } |
141 | 133 | |
142 | - private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation) { | |
143 | - ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(relation.getTo()); | |
144 | - ListenableFuture<EntityRelationInfo> entityRelationInfo = | |
145 | - Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> { | |
146 | - EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); | |
147 | - entityRelationInfo1.setToName(entityName1); | |
148 | - return entityRelationInfo1; | |
149 | - }); | |
150 | - return entityRelationInfo; | |
151 | - } | |
152 | - | |
153 | 134 | @Override |
154 | 135 | public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
155 | 136 | log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); |
... | ... | @@ -168,6 +149,38 @@ public class BaseRelationService implements RelationService { |
168 | 149 | } |
169 | 150 | |
170 | 151 | @Override |
152 | + public ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup) { | |
153 | + log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup); | |
154 | + validate(to); | |
155 | + validateTypeGroup(typeGroup); | |
156 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup); | |
157 | + ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, | |
158 | + (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { | |
159 | + List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | |
160 | + relations1.stream().forEach(relation -> | |
161 | + futures.add(fetchRelationInfoAsync(relation, | |
162 | + relation2 -> relation2.getFrom(), | |
163 | + (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) | |
164 | + ); | |
165 | + return Futures.successfulAsList(futures); | |
166 | + }); | |
167 | + return relationsInfo; | |
168 | + } | |
169 | + | |
170 | + private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation, | |
171 | + Function<EntityRelation, EntityId> entityIdGetter, | |
172 | + BiConsumer<EntityRelationInfo, String> entityNameSetter) { | |
173 | + ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation)); | |
174 | + ListenableFuture<EntityRelationInfo> entityRelationInfo = | |
175 | + Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> { | |
176 | + EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); | |
177 | + entityNameSetter.accept(entityRelationInfo1, entityName1); | |
178 | + return entityRelationInfo1; | |
179 | + }); | |
180 | + return entityRelationInfo; | |
181 | + } | |
182 | + | |
183 | + @Override | |
171 | 184 | public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
172 | 185 | log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); |
173 | 186 | validate(to); | ... | ... |
... | ... | @@ -46,6 +46,8 @@ public interface RelationService { |
46 | 46 | |
47 | 47 | ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup); |
48 | 48 | |
49 | + ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup); | |
50 | + | |
49 | 51 | ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); |
50 | 52 | |
51 | 53 | ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); | ... | ... |
... | ... | @@ -28,6 +28,7 @@ function EntityRelationService($http, $q) { |
28 | 28 | findInfoByFrom: findInfoByFrom, |
29 | 29 | findByFromAndType: findByFromAndType, |
30 | 30 | findByTo: findByTo, |
31 | + findInfoByTo: findInfoByTo, | |
31 | 32 | findByToAndType: findByToAndType, |
32 | 33 | findByQuery: findByQuery |
33 | 34 | } |
... | ... | @@ -122,6 +123,18 @@ function EntityRelationService($http, $q) { |
122 | 123 | return deferred.promise; |
123 | 124 | } |
124 | 125 | |
126 | + function findInfoByTo(toId, toType) { | |
127 | + var deferred = $q.defer(); | |
128 | + var url = '/api/relations/info?toId=' + toId; | |
129 | + url += '&toType=' + toType; | |
130 | + $http.get(url, null).then(function success(response) { | |
131 | + deferred.resolve(response.data); | |
132 | + }, function fail() { | |
133 | + deferred.reject(); | |
134 | + }); | |
135 | + return deferred.promise; | |
136 | + } | |
137 | + | |
125 | 138 | function findByToAndType(toId, toType, relationType) { |
126 | 139 | var deferred = $q.defer(); |
127 | 140 | var url = '/api/relations?toId=' + toId; | ... | ... |
... | ... | @@ -55,5 +55,11 @@ |
55 | 55 | default-event-type="{{vm.types.eventType.alarm.value}}"> |
56 | 56 | </tb-event-table> |
57 | 57 | </md-tab> |
58 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | |
59 | + <tb-relation-table flex | |
60 | + entity-id="vm.grid.operatingItem().id.id" | |
61 | + entity-type="{{vm.types.entityType.customer}}"> | |
62 | + </tb-relation-table> | |
63 | + </md-tab> | |
58 | 64 | </md-tabs> |
59 | 65 | </tb-grid> | ... | ... |
... | ... | @@ -56,4 +56,10 @@ |
56 | 56 | default-event-type="{{vm.types.eventType.alarm.value}}"> |
57 | 57 | </tb-event-table> |
58 | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | |
60 | + <tb-relation-table flex | |
61 | + entity-id="vm.grid.operatingItem().id.id" | |
62 | + entity-type="{{vm.types.entityType.device}}"> | |
63 | + </tb-relation-table> | |
64 | + </md-tab> | |
59 | 65 | </tb-grid> | ... | ... |
... | ... | @@ -17,6 +17,9 @@ |
17 | 17 | --> |
18 | 18 | <div layout='row' class="tb-entity-select"> |
19 | 19 | <tb-entity-type-select style="min-width: 100px;" |
20 | + the-form="theForm" | |
21 | + ng-disabled="disabled" | |
22 | + tb-required="tbRequired" | |
20 | 23 | ng-model="model.entityType"> |
21 | 24 | </tb-entity-type-select> |
22 | 25 | <tb-entity-autocomplete flex ng-if="model.entityType" | ... | ... |
... | ... | @@ -29,6 +29,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe |
29 | 29 | var template = $templateCache.get(entityTypeSelectTemplate); |
30 | 30 | element.html(template); |
31 | 31 | |
32 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | |
33 | + | |
32 | 34 | if (angular.isDefined(attrs.hideLabel)) { |
33 | 35 | scope.showLabel = false; |
34 | 36 | } else { |
... | ... | @@ -103,6 +105,9 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe |
103 | 105 | require: "^ngModel", |
104 | 106 | link: linker, |
105 | 107 | scope: { |
108 | + theForm: '=?', | |
109 | + tbRequired: '=?', | |
110 | + disabled:'=ngDisabled', | |
106 | 111 | allowedEntityTypes: "=?" |
107 | 112 | } |
108 | 113 | }; | ... | ... |
... | ... | @@ -17,9 +17,13 @@ |
17 | 17 | --> |
18 | 18 | <md-input-container> |
19 | 19 | <label ng-if="showLabel">{{ 'entity.type' | translate }}</label> |
20 | - <md-select ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}"> | |
20 | + <md-select ng-required="tbRequired" ng-disabled="disabled" name="entityType" | |
21 | + ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}"> | |
21 | 22 | <md-option ng-repeat="type in entityTypes" ng-value="type"> |
22 | 23 | {{typeName(type) | translate}} |
23 | 24 | </md-option> |
24 | 25 | </md-select> |
25 | -</md-input-container> | |
\ No newline at end of file | ||
26 | + <div ng-messages="theForm.entityType.$error"> | |
27 | + <div ng-message="required" translate>entity.type-required</div> | |
28 | + </div> | |
29 | +</md-input-container> | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import AddAttributeDialogController from './attribute/add-attribute-dialog.contr |
27 | 27 | import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; |
28 | 28 | import AttributeTableDirective from './attribute/attribute-table.directive'; |
29 | 29 | import RelationTableDirective from './relation/relation-table.directive'; |
30 | +import RelationTypeAutocompleteDirective from './relation/relation-type-autocomplete.directive'; | |
30 | 31 | |
31 | 32 | export default angular.module('thingsboard.entity', []) |
32 | 33 | .controller('EntityAliasesController', EntityAliasesController) |
... | ... | @@ -42,4 +43,5 @@ export default angular.module('thingsboard.entity', []) |
42 | 43 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) |
43 | 44 | .directive('tbAttributeTable', AttributeTableDirective) |
44 | 45 | .directive('tbRelationTable', RelationTableDirective) |
46 | + .directive('tbRelationTypeAutocomplete', RelationTypeAutocompleteDirective) | |
45 | 47 | .name; | ... | ... |
... | ... | @@ -14,14 +14,20 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | /*@ngInject*/ |
17 | -export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) { | |
17 | +export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, direction, entityId) { | |
18 | 18 | |
19 | 19 | var vm = this; |
20 | 20 | |
21 | 21 | vm.types = types; |
22 | + vm.direction = direction; | |
23 | + vm.targetEntityId = {}; | |
22 | 24 | |
23 | 25 | vm.relation = {}; |
24 | - vm.relation.from = from; | |
26 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
27 | + vm.relation.from = entityId; | |
28 | + } else { | |
29 | + vm.relation.to = entityId; | |
30 | + } | |
25 | 31 | vm.relation.type = types.entityRelationType.contains; |
26 | 32 | |
27 | 33 | vm.add = add; |
... | ... | @@ -32,6 +38,11 @@ export default function AddRelationDialogController($scope, $mdDialog, types, en |
32 | 38 | } |
33 | 39 | |
34 | 40 | function add() { |
41 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
42 | + vm.relation.to = vm.targetEntityId; | |
43 | + } else { | |
44 | + vm.relation.from = vm.targetEntityId; | |
45 | + } | |
35 | 46 | $scope.theForm.$setPristine(); |
36 | 47 | entityRelationService.saveRelation(vm.relation).then( |
37 | 48 | function success() { | ... | ... |
... | ... | @@ -32,19 +32,16 @@ |
32 | 32 | <div class="md-dialog-content"> |
33 | 33 | <md-content class="md-padding" layout="column"> |
34 | 34 | <fieldset ng-disabled="loading"> |
35 | - <md-input-container class="md-block"> | |
36 | - <label translate>relation.relation-type</label> | |
37 | - <md-select required ng-model="vm.relation.type" ng-disabled="loading"> | |
38 | - <md-option ng-repeat="type in vm.types.entityRelationType" ng-value="type"> | |
39 | - <span>{{('relation.relation-types.' + type) | translate}}</span> | |
40 | - </md-option> | |
41 | - </md-select> | |
42 | - </md-input-container> | |
43 | - <span class="tb-small">{{'entity.entity' | translate }}</span> | |
35 | + <tb-relation-type-autocomplete ng-model="vm.relation.type" | |
36 | + tb-required="true" | |
37 | + ng-disabled="loading"> | |
38 | + </tb-relation-type-autocomplete> | |
39 | + <small>{{(vm.direction == vm.types.entitySearchDirection.from ? | |
40 | + 'relation.to-entity' : 'relation.from-entity') | translate}}</small> | |
44 | 41 | <tb-entity-select flex |
45 | 42 | the-form="theForm" |
46 | 43 | tb-required="true" |
47 | - ng-model="vm.relation.to"> | |
44 | + ng-model="vm.targetEntityId"> | |
48 | 45 | </tb-entity-select> |
49 | 46 | </fieldset> |
50 | 47 | </md-content> | ... | ... |
... | ... | @@ -45,13 +45,17 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ |
45 | 45 | |
46 | 46 | let vm = this; |
47 | 47 | |
48 | + vm.types = types; | |
49 | + | |
50 | + vm.direction = vm.types.entitySearchDirection.from; | |
51 | + | |
48 | 52 | vm.relations = []; |
49 | 53 | vm.relationsCount = 0; |
50 | 54 | vm.allRelations = []; |
51 | 55 | vm.selectedRelations = []; |
52 | 56 | |
53 | 57 | vm.query = { |
54 | - order: 'typeName', | |
58 | + order: 'type', | |
55 | 59 | limit: 5, |
56 | 60 | page: 1, |
57 | 61 | search: null |
... | ... | @@ -62,19 +66,23 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ |
62 | 66 | vm.onReorder = onReorder; |
63 | 67 | vm.onPaginate = onPaginate; |
64 | 68 | vm.addRelation = addRelation; |
65 | - vm.editRelation = editRelation; | |
66 | 69 | vm.deleteRelation = deleteRelation; |
67 | 70 | vm.deleteRelations = deleteRelations; |
68 | 71 | vm.reloadRelations = reloadRelations; |
69 | 72 | vm.updateRelations = updateRelations; |
70 | 73 | |
71 | - | |
72 | 74 | $scope.$watch("vm.entityId", function(newVal, prevVal) { |
73 | 75 | if (newVal && !angular.equals(newVal, prevVal)) { |
74 | 76 | reloadRelations(); |
75 | 77 | } |
76 | 78 | }); |
77 | 79 | |
80 | + $scope.$watch("vm.direction", function(newVal, prevVal) { | |
81 | + if (newVal && !angular.equals(newVal, prevVal)) { | |
82 | + reloadRelations(); | |
83 | + } | |
84 | + }); | |
85 | + | |
78 | 86 | $scope.$watch("vm.query.search", function(newVal, prevVal) { |
79 | 87 | if (!angular.equals(newVal, prevVal) && vm.query.search != null) { |
80 | 88 | updateRelations(); |
... | ... | @@ -102,7 +110,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ |
102 | 110 | if ($event) { |
103 | 111 | $event.stopPropagation(); |
104 | 112 | } |
105 | - var from = { | |
113 | + var entityId = { | |
106 | 114 | id: vm.entityId, |
107 | 115 | entityType: vm.entityType |
108 | 116 | }; |
... | ... | @@ -111,7 +119,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ |
111 | 119 | controllerAs: 'vm', |
112 | 120 | templateUrl: addRelationTemplate, |
113 | 121 | parent: angular.element($document[0].body), |
114 | - locals: { from: from }, | |
122 | + locals: { direction: vm.direction, entityId: entityId }, | |
115 | 123 | fullscreen: true, |
116 | 124 | targetEvent: $event |
117 | 125 | }).then(function () { |
... | ... | @@ -120,36 +128,100 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ |
120 | 128 | }); |
121 | 129 | } |
122 | 130 | |
123 | - function editRelation($event, /*relation*/) { | |
131 | + function deleteRelation($event, relation) { | |
124 | 132 | if ($event) { |
125 | 133 | $event.stopPropagation(); |
126 | 134 | } |
127 | - //TODO: | |
128 | - } | |
135 | + if (relation) { | |
136 | + var title; | |
137 | + var content; | |
138 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
139 | + title = $translate.instant('relation.delete-to-relation-title', {entityName: relation.toName}); | |
140 | + content = $translate.instant('relation.delete-to-relation-text', {entityName: relation.toName}); | |
141 | + } else { | |
142 | + title = $translate.instant('relation.delete-from-relation-title', {entityName: relation.fromName}); | |
143 | + content = $translate.instant('relation.delete-from-relation-text', {entityName: relation.fromName}); | |
144 | + } | |
129 | 145 | |
130 | - function deleteRelation($event, /*relation*/) { | |
131 | - if ($event) { | |
132 | - $event.stopPropagation(); | |
146 | + var confirm = $mdDialog.confirm() | |
147 | + .targetEvent($event) | |
148 | + .title(title) | |
149 | + .htmlContent(content) | |
150 | + .ariaLabel(title) | |
151 | + .cancel($translate.instant('action.no')) | |
152 | + .ok($translate.instant('action.yes')); | |
153 | + $mdDialog.show(confirm).then(function () { | |
154 | + entityRelationService.deleteRelation( | |
155 | + relation.from.id, | |
156 | + relation.from.entityType, | |
157 | + relation.type, | |
158 | + relation.to.id, | |
159 | + relation.to.entityType).then( | |
160 | + function success() { | |
161 | + reloadRelations(); | |
162 | + } | |
163 | + ); | |
164 | + }); | |
133 | 165 | } |
134 | - //TODO: | |
135 | 166 | } |
136 | 167 | |
137 | 168 | function deleteRelations($event) { |
138 | 169 | if ($event) { |
139 | 170 | $event.stopPropagation(); |
140 | 171 | } |
141 | - //TODO: | |
172 | + if (vm.selectedRelations && vm.selectedRelations.length > 0) { | |
173 | + var title; | |
174 | + var content; | |
175 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
176 | + title = $translate.instant('relation.delete-to-relations-title', {count: vm.selectedRelations.length}, 'messageformat'); | |
177 | + content = $translate.instant('relation.delete-to-relations-text'); | |
178 | + } else { | |
179 | + title = $translate.instant('relation.delete-from-relations-title', {count: vm.selectedRelations.length}, 'messageformat'); | |
180 | + content = $translate.instant('relation.delete-from-relations-text'); | |
181 | + } | |
182 | + var confirm = $mdDialog.confirm() | |
183 | + .targetEvent($event) | |
184 | + .title(title) | |
185 | + .htmlContent(content) | |
186 | + .ariaLabel(title) | |
187 | + .cancel($translate.instant('action.no')) | |
188 | + .ok($translate.instant('action.yes')); | |
189 | + $mdDialog.show(confirm).then(function () { | |
190 | + var tasks = []; | |
191 | + for (var i=0;i<vm.selectedRelations.length;i++) { | |
192 | + var relation = vm.selectedRelations[i]; | |
193 | + tasks.push( entityRelationService.deleteRelation( | |
194 | + relation.from.id, | |
195 | + relation.from.entityType, | |
196 | + relation.type, | |
197 | + relation.to.id, | |
198 | + relation.to.entityType)); | |
199 | + } | |
200 | + $q.all(tasks).then(function () { | |
201 | + reloadRelations(); | |
202 | + }); | |
203 | + | |
204 | + }); | |
205 | + } | |
142 | 206 | } |
143 | 207 | |
144 | 208 | function reloadRelations () { |
145 | 209 | vm.allRelations.length = 0; |
146 | 210 | vm.relations.length = 0; |
147 | - vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); | |
211 | + vm.relationsPromise; | |
212 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
213 | + vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); | |
214 | + } else { | |
215 | + vm.relationsPromise = entityRelationService.findInfoByTo(vm.entityId, vm.entityType); | |
216 | + } | |
148 | 217 | vm.relationsPromise.then( |
149 | 218 | function success(allRelations) { |
150 | 219 | allRelations.forEach(function(relation) { |
151 | - relation.typeName = $translate.instant('relation.relation-type.' + relation.type); | |
152 | - relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); | |
220 | + if (vm.direction == vm.types.entitySearchDirection.from) { | |
221 | + relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); | |
222 | + } else { | |
223 | + relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType)); | |
224 | + } | |
153 | 225 | }); |
154 | 226 | vm.allRelations = allRelations; |
155 | 227 | vm.selectedRelations = []; | ... | ... |
... | ... | @@ -16,11 +16,22 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 18 | <md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column"> |
19 | + <section layout="row"> | |
20 | + <md-input-container class="md-block" style="width: 200px;"> | |
21 | + <label translate>relation.direction</label> | |
22 | + <md-select ng-model="vm.direction" ng-disabled="loading"> | |
23 | + <md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction"> | |
24 | + {{ ('relation.search-direction.' + direction) | translate}} | |
25 | + </md-option> | |
26 | + </md-select> | |
27 | + </md-input-container> | |
28 | + </section> | |
19 | 29 | <div layout="column" class="md-whiteframe-z1"> |
20 | 30 | <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length |
21 | 31 | && vm.query.search === null"> |
22 | 32 | <div class="md-toolbar-tools"> |
23 | - <span translate>relation.entity-relations</span> | |
33 | + <span>{{(vm.direction == vm.types.entitySearchDirection.from ? | |
34 | + 'relation.from-relations' : 'relation.to-relations') | translate}}</span> | |
24 | 35 | <span flex></span> |
25 | 36 | <md-button class="md-icon-button" ng-click="vm.addRelation($event)"> |
26 | 37 | <md-icon>add</md-icon> |
... | ... | @@ -66,7 +77,7 @@ |
66 | 77 | <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length"> |
67 | 78 | <div class="md-toolbar-tools"> |
68 | 79 | <span translate |
69 | - translate-values="{count: selectedRelations.length}" | |
80 | + translate-values="{count: vm.selectedRelations.length}" | |
70 | 81 | translate-interpolation="messageformat">relation.selected-relations</span> |
71 | 82 | <span flex></span> |
72 | 83 | <md-button class="md-icon-button" ng-click="vm.deleteRelations($event)"> |
... | ... | @@ -81,25 +92,26 @@ |
81 | 92 | <table md-table md-row-select multiple="" ng-model="vm.selectedRelations" md-progress="vm.relationsDeferred.promise"> |
82 | 93 | <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> |
83 | 94 | <tr md-row> |
84 | - <th md-column md-order-by="typeName"><span translate>relation.type</span></th> | |
85 | - <th md-column md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th> | |
86 | - <th md-column md-order-by="toName"><span translate>relation.to-entity-name</span></th> | |
95 | + <th md-column md-order-by="type"><span translate>relation.type</span></th> | |
96 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from" | |
97 | + md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th> | |
98 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to" | |
99 | + md-order-by="fromEntityTypeName"><span translate>relation.from-entity-type</span></th> | |
100 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from" | |
101 | + md-order-by="toName"><span translate>relation.to-entity-name</span></th> | |
102 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to" | |
103 | + md-order-by="fromName"><span translate>relation.from-entity-name</span></th> | |
87 | 104 | <th md-column><span> </span></th> |
88 | 105 | </tr> |
89 | 106 | </thead> |
90 | 107 | <tbody md-body> |
91 | 108 | <tr md-row md-select="relation" md-select-id="relation" md-auto-select ng-repeat="relation in vm.relations"> |
92 | - <td md-cell>{{ relation.typeName }}</td> | |
93 | - <td md-cell>{{ relation.toEntityTypeName }}</td> | |
94 | - <td md-cell>{{ relation.toName }}</td> | |
109 | + <td md-cell>{{ relation.type }}</td> | |
110 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toEntityTypeName }}</td> | |
111 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromEntityTypeName }}</td> | |
112 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toName }}</td> | |
113 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromName }}</td> | |
95 | 114 | <td md-cell class="tb-action-cell"> |
96 | - <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" | |
97 | - ng-click="vm.editRelation($event, relation)"> | |
98 | - <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> | |
99 | - <md-tooltip md-direction="top"> | |
100 | - {{ 'relation.edit' | translate }} | |
101 | - </md-tooltip> | |
102 | - </md-button> | |
103 | 115 | <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)"> |
104 | 116 | <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon> |
105 | 117 | <md-tooltip md-direction="top"> | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +import './relation-type-autocomplete.scss'; | |
17 | + | |
18 | +/* eslint-disable import/no-unresolved, import/default */ | |
19 | + | |
20 | +import relationTypeAutocompleteTemplate from './relation-type-autocomplete.tpl.html'; | |
21 | + | |
22 | +/* eslint-enable import/no-unresolved, import/default */ | |
23 | + | |
24 | +/*@ngInject*/ | |
25 | +export default function RelationTypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, types) { | |
26 | + | |
27 | + var linker = function (scope, element, attrs, ngModelCtrl) { | |
28 | + var template = $templateCache.get(relationTypeAutocompleteTemplate); | |
29 | + element.html(template); | |
30 | + | |
31 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | |
32 | + scope.relationType = null; | |
33 | + scope.relationTypeSearchText = ''; | |
34 | + scope.relationTypes = []; | |
35 | + for (var type in types.entityRelationType) { | |
36 | + scope.relationTypes.push(types.entityRelationType[type]); | |
37 | + } | |
38 | + | |
39 | + scope.fetchRelationTypes = function(searchText) { | |
40 | + var deferred = $q.defer(); | |
41 | + var result = $filter('filter')(scope.relationTypes, {'$': searchText}); | |
42 | + if (result && result.length) { | |
43 | + deferred.resolve(result); | |
44 | + } else { | |
45 | + deferred.resolve([searchText]); | |
46 | + } | |
47 | + return deferred.promise; | |
48 | + } | |
49 | + | |
50 | + scope.relationTypeSearchTextChanged = function() { | |
51 | + } | |
52 | + | |
53 | + scope.updateView = function () { | |
54 | + if (!scope.disabled) { | |
55 | + ngModelCtrl.$setViewValue(scope.relationType); | |
56 | + } | |
57 | + } | |
58 | + | |
59 | + ngModelCtrl.$render = function () { | |
60 | + scope.relationType = ngModelCtrl.$viewValue; | |
61 | + } | |
62 | + | |
63 | + scope.$watch('relationType', function (newValue, prevValue) { | |
64 | + if (!angular.equals(newValue, prevValue)) { | |
65 | + scope.updateView(); | |
66 | + } | |
67 | + }); | |
68 | + | |
69 | + scope.$watch('disabled', function () { | |
70 | + scope.updateView(); | |
71 | + }); | |
72 | + | |
73 | + $compile(element.contents())(scope); | |
74 | + } | |
75 | + | |
76 | + return { | |
77 | + restrict: "E", | |
78 | + require: "^ngModel", | |
79 | + link: linker, | |
80 | + scope: { | |
81 | + theForm: '=?', | |
82 | + tbRequired: '=?', | |
83 | + disabled:'=ngDisabled' | |
84 | + } | |
85 | + }; | |
86 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +.tb-relation-type-autocomplete { | |
17 | + .tb-relation-type-item { | |
18 | + display: block; | |
19 | + height: 48px; | |
20 | + } | |
21 | + li { | |
22 | + height: auto !important; | |
23 | + white-space: normal !important; | |
24 | + } | |
25 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-autocomplete ng-required="tbRequired" | |
19 | + ng-disabled="disabled" | |
20 | + md-no-cache="true" | |
21 | + md-input-name="relationType" | |
22 | + ng-model="relationType" | |
23 | + md-selected-item="relationType" | |
24 | + md-search-text="relationTypeSearchText" | |
25 | + md-search-text-change="relationTypeSearchTextChanged()" | |
26 | + md-items="item in fetchRelationTypes(relationTypeSearchText)" | |
27 | + md-item-text="item" | |
28 | + md-min-length="0" | |
29 | + md-floating-label="{{ 'relation.relation-type' | translate }}" | |
30 | + md-select-on-match="true" | |
31 | + md-menu-class="tb-relation-type-autocomplete"> | |
32 | + <md-item-template> | |
33 | + <div class="tb-relation-type-item"> | |
34 | + <span md-highlight-text="relationTypeSearchText" md-highlight-flags="^i">{{item}}</span> | |
35 | + </div> | |
36 | + </md-item-template> | |
37 | + <div ng-messages="theForm.relationType.$error"> | |
38 | + <div translate ng-message="required">relation.relation-type-required</div> | |
39 | + </div> | |
40 | +</md-autocomplete> | ... | ... |
... | ... | @@ -544,6 +544,7 @@ export default angular.module('thingsboard.locale', []) |
544 | 544 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", |
545 | 545 | "all-subtypes": "All", |
546 | 546 | "type": "Type", |
547 | + "type-required": "Entity type is required.", | |
547 | 548 | "type-device": "Device", |
548 | 549 | "type-asset": "Asset", |
549 | 550 | "type-rule": "Rule", |
... | ... | @@ -718,19 +719,33 @@ export default angular.module('thingsboard.locale', []) |
718 | 719 | }, |
719 | 720 | "relation": { |
720 | 721 | "relations": "Relations", |
721 | - "entity-relations": "Entity relations", | |
722 | + "direction": "Direction", | |
723 | + "search-direction": { | |
724 | + "FROM": "From", | |
725 | + "TO": "To" | |
726 | + }, | |
727 | + "from-relations": "Outbound relations", | |
728 | + "to-relations": "Inbound relations", | |
722 | 729 | "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected", |
723 | 730 | "type": "Type", |
724 | - "to-entity-type": "Entity type", | |
725 | - "to-entity-name": "Entity name", | |
726 | - "edit": "Edit relation", | |
731 | + "to-entity-type": "To entity type", | |
732 | + "to-entity-name": "To entity name", | |
733 | + "from-entity-type": "From entity type", | |
734 | + "from-entity-name": "From entity name", | |
735 | + "to-entity": "To entity", | |
736 | + "from-entity": "From entity", | |
727 | 737 | "delete": "Delete relation", |
728 | 738 | "relation-type": "Relation type", |
729 | - "relation-types": { | |
730 | - "Contains": "Contains", | |
731 | - "Manages": "Manages" | |
732 | - }, | |
733 | - "add": "Add relation" | |
739 | + "relation-type-required": "Relation type is required.", | |
740 | + "add": "Add relation", | |
741 | + "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?", | |
742 | + "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.", | |
743 | + "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", | |
744 | + "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.", | |
745 | + "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?", | |
746 | + "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.", | |
747 | + "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", | |
748 | + "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." | |
734 | 749 | }, |
735 | 750 | "rule": { |
736 | 751 | "rule": "Rule", | ... | ... |
... | ... | @@ -56,5 +56,11 @@ |
56 | 56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> |
57 | 57 | </tb-event-table> |
58 | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | |
60 | + <tb-relation-table flex | |
61 | + entity-id="vm.grid.operatingItem().id.id" | |
62 | + entity-type="{{vm.types.entityType.plugin}}"> | |
63 | + </tb-relation-table> | |
64 | + </md-tab> | |
59 | 65 | </md-tabs> |
60 | 66 | </tb-grid> | ... | ... |
... | ... | @@ -56,5 +56,11 @@ |
56 | 56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> |
57 | 57 | </tb-event-table> |
58 | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | |
60 | + <tb-relation-table flex | |
61 | + entity-id="vm.grid.operatingItem().id.id" | |
62 | + entity-type="{{vm.types.entityType.rule}}"> | |
63 | + </tb-relation-table> | |
64 | + </md-tab> | |
59 | 65 | </md-tabs> |
60 | 66 | </tb-grid> | ... | ... |
... | ... | @@ -53,5 +53,11 @@ |
53 | 53 | default-event-type="{{vm.types.eventType.alarm.value}}"> |
54 | 54 | </tb-event-table> |
55 | 55 | </md-tab> |
56 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | |
57 | + <tb-relation-table flex | |
58 | + entity-id="vm.grid.operatingItem().id.id" | |
59 | + entity-type="{{vm.types.entityType.tenant}}"> | |
60 | + </tb-relation-table> | |
61 | + </md-tab> | |
56 | 62 | </md-tabs> |
57 | 63 | </tb-grid> | ... | ... |