Commit 9ef646765e4bf4e07396b55f70534ca37671cd36

Authored by Igor Kulikov
1 parent 0a83091a

TB-61: Improve Aliases management. Implement Asset type and Device type alias filter.

@@ -95,8 +95,7 @@ export default class AliasController { @@ -95,8 +95,7 @@ export default class AliasController {
95 this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( 95 this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
96 function success(aliasInfo) { 96 function success(aliasInfo) {
97 aliasCtrl.resolvedAliases[aliasId] = aliasInfo; 97 aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
98 - if (entityAlias.filter.stateEntity) {  
99 - aliasInfo.stateEntity = true; 98 + if (aliasInfo.stateEntity) {
100 aliasCtrl.resolvedAliasesToStateEntities[aliasId] = 99 aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
101 aliasCtrl.stateController.getStateParams().entityId; 100 aliasCtrl.stateController.getStateParams().entityId;
102 } 101 }
@@ -31,6 +31,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -31,6 +31,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
31 resolveAliasFilter: resolveAliasFilter, 31 resolveAliasFilter: resolveAliasFilter,
32 checkEntityAlias: checkEntityAlias, 32 checkEntityAlias: checkEntityAlias,
33 filterAliasByEntityTypes: filterAliasByEntityTypes, 33 filterAliasByEntityTypes: filterAliasByEntityTypes,
  34 + getAliasFilterTypesByEntityTypes: getAliasFilterTypesByEntityTypes,
34 getEntityKeys: getEntityKeys, 35 getEntityKeys: getEntityKeys,
35 createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo, 36 createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
36 getRelatedEntities: getRelatedEntities, 37 getRelatedEntities: getRelatedEntities,
@@ -223,17 +224,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -223,17 +224,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
223 return promise; 224 return promise;
224 } 225 }
225 226
226 - function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {  
227 - var deferred = $q.defer();  
228 - var pageLink = {limit: limit, textSearch: entityNameFilter}; 227 + function getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred) {
229 var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); 228 var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
230 if (promise) { 229 if (promise) {
231 promise.then( 230 promise.then(
232 function success(result) { 231 function success(result) {
233 - if (result.data && result.data.length > 0) {  
234 - deferred.resolve(result.data); 232 + data = data.concat(result.data);
  233 + if (result.hasNext) {
  234 + pageLink = result.nextPageLink;
  235 + getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
235 } else { 236 } else {
236 - deferred.resolve(null); 237 + if (data && data.length > 0) {
  238 + deferred.resolve(data);
  239 + } else {
  240 + deferred.resolve(null);
  241 + }
237 } 242 }
238 }, 243 },
239 function fail() { 244 function fail() {
@@ -243,6 +248,34 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -243,6 +248,34 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
243 } else { 248 } else {
244 deferred.resolve(null); 249 deferred.resolve(null);
245 } 250 }
  251 + }
  252 +
  253 + function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
  254 + var deferred = $q.defer();
  255 + var pageLink = {limit: limit, textSearch: entityNameFilter};
  256 + if (limit == -1) { // all
  257 + var data = [];
  258 + pageLink.limit = 100;
  259 + getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
  260 + } else {
  261 + var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
  262 + if (promise) {
  263 + promise.then(
  264 + function success(result) {
  265 + if (result.data && result.data.length > 0) {
  266 + deferred.resolve(result.data);
  267 + } else {
  268 + deferred.resolve(null);
  269 + }
  270 + },
  271 + function fail() {
  272 + deferred.resolve(null);
  273 + }
  274 + );
  275 + } else {
  276 + deferred.resolve(null);
  277 + }
  278 + }
246 return deferred.promise; 279 return deferred.promise;
247 } 280 }
248 281
@@ -261,11 +294,12 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -261,11 +294,12 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
261 function resolveAlias(entityAlias, stateParams) { 294 function resolveAlias(entityAlias, stateParams) {
262 var deferred = $q.defer(); 295 var deferred = $q.defer();
263 var filter = entityAlias.filter; 296 var filter = entityAlias.filter;
264 - resolveAliasFilter(filter, stateParams, 100).then( 297 + resolveAliasFilter(filter, stateParams, -1).then(
265 function (result) { 298 function (result) {
266 var entities = result.entities; 299 var entities = result.entities;
267 var aliasInfo = { 300 var aliasInfo = {
268 alias: entityAlias.alias, 301 alias: entityAlias.alias,
  302 + stateEntity: result.stateEntity,
269 resolveMultiple: filter.resolveMultiple 303 resolveMultiple: filter.resolveMultiple
270 }; 304 };
271 var resolvedEntities = entitiesToEntitiesInfo(entities); 305 var resolvedEntities = entitiesToEntitiesInfo(entities);
@@ -291,39 +325,68 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -291,39 +325,68 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
291 }; 325 };
292 switch (filter.type) { 326 switch (filter.type) {
293 case types.aliasFilterType.entityList.value: 327 case types.aliasFilterType.entityList.value:
294 - if (filter.stateEntity) {  
295 - result.stateEntity = true;  
296 - if (stateParams && stateParams.entityId) {  
297 - getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(  
298 - function success(entity) {  
299 - result.entities = [entity];  
300 - deferred.resolve(result);  
301 - },  
302 - function fail() {  
303 - deferred.reject();  
304 - }  
305 - );  
306 - } else {  
307 - deferred.resolve(result); 328 + getEntities(filter.entityType, filter.entityList).then(
  329 + function success(entities) {
  330 + if (entities && entities.length) {
  331 + result.entities = entities;
  332 + deferred.resolve(result);
  333 + } else {
  334 + deferred.reject();
  335 + }
  336 + },
  337 + function fail() {
  338 + deferred.reject();
308 } 339 }
309 - } else {  
310 - getEntities(filter.entityType, filter.entityList).then(  
311 - function success(entities) {  
312 - if (entities && entities.length) {  
313 - result.entities = entities;  
314 - deferred.resolve(result);  
315 - } else {  
316 - deferred.reject();  
317 - } 340 + );
  341 + break;
  342 + case types.aliasFilterType.entityName.value:
  343 + getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
  344 + function success(entities) {
  345 + if (entities && entities.length) {
  346 + result.entities = entities;
  347 + deferred.resolve(result);
  348 + } else {
  349 + deferred.reject();
  350 + }
  351 + },
  352 + function fail() {
  353 + deferred.reject();
  354 + }
  355 + );
  356 + break;
  357 + case types.aliasFilterType.stateEntity.value:
  358 + result.stateEntity = true;
  359 + if (stateParams && stateParams.entityId) {
  360 + getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
  361 + function success(entity) {
  362 + result.entities = [entity];
  363 + deferred.resolve(result);
318 }, 364 },
319 function fail() { 365 function fail() {
320 deferred.reject(); 366 deferred.reject();
321 } 367 }
322 ); 368 );
  369 + } else {
  370 + deferred.resolve(result);
323 } 371 }
324 break; 372 break;
325 - case types.aliasFilterType.entityName.value:  
326 - getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then( 373 + case types.aliasFilterType.assetType.value:
  374 + getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
  375 + function success(entities) {
  376 + if (entities && entities.length) {
  377 + result.entities = entities;
  378 + deferred.resolve(result);
  379 + } else {
  380 + deferred.reject();
  381 + }
  382 + },
  383 + function fail() {
  384 + deferred.reject();
  385 + }
  386 + );
  387 + break;
  388 + case types.aliasFilterType.deviceType.value:
  389 + getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
327 function success(entities) { 390 function success(entities) {
328 if (entities && entities.length) { 391 if (entities && entities.length) {
329 result.entities = entities; 392 result.entities = entities;
@@ -337,27 +400,81 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -337,27 +400,81 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
337 } 400 }
338 ); 401 );
339 break; 402 break;
340 - //TODO: 403 +
  404 + //TODO: Alias filter
341 } 405 }
342 return deferred.promise; 406 return deferred.promise;
343 } 407 }
344 408
345 function filterAliasByEntityTypes(entityAlias, entityTypes) { 409 function filterAliasByEntityTypes(entityAlias, entityTypes) {
346 var filter = entityAlias.filter; 410 var filter = entityAlias.filter;
347 - switch (filter.type) {  
348 - case types.aliasFilterType.entityList.value:  
349 - if (filter.stateEntity) {  
350 - return true;  
351 - } else { 411 + if (filterAliasFilterTypeByEntityTypes(filter.type, entityTypes)) {
  412 + switch (filter.type) {
  413 + case types.aliasFilterType.entityList.value:
352 return entityTypes.indexOf(filter.entityType) > -1 ? true : false; 414 return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
353 - } 415 + case types.aliasFilterType.entityName.value:
  416 + return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
  417 + case types.aliasFilterType.stateEntity.value:
  418 + return true;
  419 + case types.aliasFilterType.assetType.value:
  420 + return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false;
  421 + case types.aliasFilterType.deviceType.value:
  422 + return entityTypes.indexOf(types.entityType.device) > -1 ? true : false;
  423 + }
  424 + }
  425 + //TODO: Alias filter
  426 + return false;
  427 + }
  428 +
  429 + function filterAliasFilterTypeByEntityType(aliasFilterType, entityType) {
  430 + switch (aliasFilterType) {
  431 + case types.aliasFilterType.entityList.value:
  432 + return true;
354 case types.aliasFilterType.entityName.value: 433 case types.aliasFilterType.entityName.value:
355 - return entityTypes.indexOf(filter.entityType) > -1 ? true : false; 434 + return true;
  435 + case types.aliasFilterType.stateEntity.value:
  436 + return true;
  437 + case types.aliasFilterType.assetType.value:
  438 + return entityType === types.entityType.asset;
  439 + case types.aliasFilterType.deviceType.value:
  440 + return entityType === types.entityType.device;
  441 + case types.aliasFilterType.relationsQuery.value:
  442 + return true;
  443 + case types.aliasFilterType.assetSearchQuery.value:
  444 + return entityType === types.entityType.asset;
  445 + case types.aliasFilterType.deviceSearchQuery.value:
  446 + return entityType === types.entityType.device;
356 } 447 }
357 - //TODO:  
358 return false; 448 return false;
359 } 449 }
360 450
  451 + function filterAliasFilterTypeByEntityTypes(aliasFilterType, entityTypes) {
  452 + if (!entityTypes || !entityTypes.length) {
  453 + return true;
  454 + }
  455 + var valid = false;
  456 + entityTypes.forEach(function(entityType) {
  457 + valid = valid || filterAliasFilterTypeByEntityType(aliasFilterType, entityType);
  458 + });
  459 + return valid;
  460 + }
  461 +
  462 + function getAliasFilterTypesByEntityTypes(entityTypes) {
  463 + var allAliasFilterTypes = types.aliasFilterType;
  464 + if (!entityTypes || !entityTypes.length) {
  465 + return allAliasFilterTypes;
  466 + }
  467 + var result = {};
  468 + for (var type in allAliasFilterTypes) {
  469 + var aliasFilterType = allAliasFilterTypes[type];
  470 + if (filterAliasFilterTypeByEntityTypes(aliasFilterType.value, entityTypes)) {
  471 + result[type] = aliasFilterType;
  472 + }
  473 + }
  474 + return result;
  475 + }
  476 +
  477 +
361 function checkEntityAlias(entityAlias) { 478 function checkEntityAlias(entityAlias) {
362 var deferred = $q.defer(); 479 var deferred = $q.defer();
363 resolveAliasFilter(entityAlias.filter, null, 1).then( 480 resolveAliasFilter(entityAlias.filter, null, 1).then(
@@ -380,50 +497,6 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -380,50 +497,6 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
380 return deferred.promise; 497 return deferred.promise;
381 } 498 }
382 499
383 - /*function processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred) {  
384 - if (index < aliasIds.length) {  
385 - var aliasId = aliasIds[index];  
386 - var entityAlias = entityAliases[aliasId];  
387 - var alias = entityAlias.alias;  
388 - var filter = entityAlias.filter;  
389 - resolveAliasFilter(filter, stateParams).then(  
390 - function (entities) {  
391 - if (entities && entities.length) {  
392 - var entity = entities[0];  
393 - var resolvedAlias = {alias: alias, entityType: entity.id.entityType, entityId: entity.id.id};  
394 - resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;  
395 - resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entities);  
396 - index++;  
397 - processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);  
398 - } else {  
399 - if (!resolution.error) {  
400 - resolution.error = 'dashboard.invalid-aliases-config';  
401 - }  
402 - index++;  
403 - processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);  
404 - }  
405 - }  
406 - );  
407 - } else {  
408 - deferred.resolve(resolution);  
409 - }  
410 - }*/  
411 -  
412 - /*function processEntityAliases(entityAliases, stateParams) {  
413 - var deferred = $q.defer();  
414 - var resolution = {  
415 - aliasesInfo: {}  
416 - };  
417 - var aliasIds = [];  
418 - if (entityAliases) {  
419 - for (var aliasId in entityAliases) {  
420 - aliasIds.push(aliasId);  
421 - }  
422 - }  
423 - processEntityAlias(0, aliasIds, entityAliases, stateParams, resolution, deferred);  
424 - return deferred.promise;  
425 - }*/  
426 -  
427 function getEntityKeys(entityType, entityId, query, type) { 500 function getEntityKeys(entityType, entityId, query, type) {
428 var deferred = $q.defer(); 501 var deferred = $q.defer();
429 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; 502 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
@@ -889,4 +962,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -889,4 +962,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
889 } 962 }
890 } 963 }
891 964
892 -}  
  965 +}
@@ -258,17 +258,17 @@ export default class Subscription { @@ -258,17 +258,17 @@ export default class Subscription {
258 } else { 258 } else {
259 subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false; 259 subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false;
260 } 260 }
261 - subscription.callbacks.rpcStateChanged(this); 261 + subscription.callbacks.rpcStateChanged(subscription);
262 deferred.resolve(); 262 deferred.resolve();
263 } else { 263 } else {
264 subscription.rpcEnabled = false; 264 subscription.rpcEnabled = false;
265 - subscription.callbacks.rpcStateChanged(this); 265 + subscription.callbacks.rpcStateChanged(subscription);
266 deferred.resolve(); 266 deferred.resolve();
267 } 267 }
268 }, 268 },
269 function fail () { 269 function fail () {
270 subscription.rpcEnabled = false; 270 subscription.rpcEnabled = false;
271 - subscription.callbacks.rpcStateChanged(this); 271 + subscription.callbacks.rpcStateChanged(subscription);
272 deferred.resolve(); 272 deferred.resolve();
273 } 273 }
274 ); 274 );
@@ -110,14 +110,12 @@ function DashboardUtils(types, utils, timeService) { @@ -110,14 +110,12 @@ function DashboardUtils(types, utils, timeService) {
110 deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value; 110 deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value;
111 if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { 111 if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
112 entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList; 112 entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList;
113 - entityAlias.filter.stateEntity = false;  
114 } else { 113 } else {
115 entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter; 114 entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
116 } 115 }
117 } else { 116 } else {
118 entityAlias.filter.type = types.aliasFilterType.entityList.value; 117 entityAlias.filter.type = types.aliasFilterType.entityList.value;
119 entityAlias.filter.entityList = [deviceAlias.deviceId]; 118 entityAlias.filter.entityList = [deviceAlias.deviceId];
120 - entityAlias.filter.stateEntity = false;  
121 } 119 }
122 return entityAlias; 120 return entityAlias;
123 } 121 }
@@ -132,7 +130,6 @@ function DashboardUtils(types, utils, timeService) { @@ -132,7 +130,6 @@ function DashboardUtils(types, utils, timeService) {
132 } 130 }
133 if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { 131 if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
134 entityAlias.filter.entityList = entityAlias.entityFilter.entityList; 132 entityAlias.filter.entityList = entityAlias.entityFilter.entityList;
135 - entityAlias.filter.stateEntity = false;  
136 } else { 133 } else {
137 entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter; 134 entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter;
138 } 135 }
@@ -342,7 +339,6 @@ function DashboardUtils(types, utils, timeService) { @@ -342,7 +339,6 @@ function DashboardUtils(types, utils, timeService) {
342 function createSingleEntityFilter(entityType, entityId) { 339 function createSingleEntityFilter(entityType, entityId) {
343 return { 340 return {
344 type: types.aliasFilterType.entityList.value, 341 type: types.aliasFilterType.entityList.value,
345 - stateEntity: false,  
346 entityList: [entityId], 342 entityList: [entityId],
347 entityType: entityType, 343 entityType: entityType,
348 resolveMultiple: false 344 resolveMultiple: false
@@ -74,6 +74,10 @@ export default angular.module('thingsboard.types', []) @@ -74,6 +74,10 @@ export default angular.module('thingsboard.types', [])
74 value: 'entityName', 74 value: 'entityName',
75 name: 'alias.filter-type-entity-name' 75 name: 'alias.filter-type-entity-name'
76 }, 76 },
  77 + stateEntity: {
  78 + value: 'stateEntity',
  79 + name: 'alias.filter-type-state-entity'
  80 + },
77 assetType: { 81 assetType: {
78 value: 'assetType', 82 value: 'assetType',
79 name: 'alias.filter-type-asset-type' 83 name: 'alias.filter-type-asset-type'
@@ -139,6 +143,53 @@ export default angular.module('thingsboard.types', []) @@ -139,6 +143,53 @@ export default angular.module('thingsboard.types', [])
139 dashboard: "DASHBOARD", 143 dashboard: "DASHBOARD",
140 alarm: "ALARM" 144 alarm: "ALARM"
141 }, 145 },
  146 + entityTypeTranslations: {
  147 + "DEVICE": {
  148 + type: 'entity.type-device',
  149 + list: 'entity.list-of-devices',
  150 + nameStartsWith: 'entity.device-name-starts-with'
  151 + },
  152 + "ASSET": {
  153 + type: 'entity.type-asset',
  154 + list: 'entity.list-of-assets',
  155 + nameStartsWith: 'entity.asset-name-starts-with'
  156 + },
  157 + "RULE": {
  158 + type: 'entity.type-rule',
  159 + list: 'entity.list-of-rules',
  160 + nameStartsWith: 'entity.rule-name-starts-with'
  161 + },
  162 + "PLUGIN": {
  163 + type: 'entity.type-plugin',
  164 + list: 'entity.list-of-plugins',
  165 + nameStartsWith: 'entity.plugin-name-starts-with'
  166 + },
  167 + "TENANT": {
  168 + type: 'entity.type-tenant',
  169 + list: 'entity.list-of-tenants',
  170 + nameStartsWith: 'entity.tenant-name-starts-with'
  171 + },
  172 + "CUSTOMER": {
  173 + type: 'entity.type-customer',
  174 + list: 'entity.list-of-customers',
  175 + nameStartsWith: 'entity.customer-name-starts-with'
  176 + },
  177 + "USER": {
  178 + type: 'entity.type-user',
  179 + list: 'entity.list-of-users',
  180 + nameStartsWith: 'entity.user-name-starts-with'
  181 + },
  182 + "DASHBOARD": {
  183 + type: 'entity.type-dashboard',
  184 + list: 'entity.list-of-dashboards',
  185 + nameStartsWith: 'entity.dashboard-name-starts-with'
  186 + },
  187 + "ALARM": {
  188 + type: 'entity.type-alarm',
  189 + list: 'entity.list-of-alarms',
  190 + nameStartsWith: 'entity.alarm-name-starts-with'
  191 + }
  192 + },
142 entitySearchDirection: { 193 entitySearchDirection: {
143 from: "FROM", 194 from: "FROM",
144 to: "TO" 195 to: "TO"
@@ -109,8 +109,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) { @@ -109,8 +109,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
109 cleanCopy: cleanCopy, 109 cleanCopy: cleanCopy,
110 isLocalUrl: isLocalUrl, 110 isLocalUrl: isLocalUrl,
111 validateDatasources: validateDatasources, 111 validateDatasources: validateDatasources,
112 - createKey: createKey,  
113 - entityTypeName: entityTypeName 112 + createKey: createKey
114 } 113 }
115 114
116 return service; 115 return service;
@@ -358,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) { @@ -358,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
358 return dataKey; 357 return dataKey;
359 } 358 }
360 359
361 - function entityTypeName (type) {  
362 - switch (type) {  
363 - case types.entityType.device:  
364 - return 'entity.type-device';  
365 - case types.entityType.asset:  
366 - return 'entity.type-asset';  
367 - case types.entityType.rule:  
368 - return 'entity.type-rule';  
369 - case types.entityType.plugin:  
370 - return 'entity.type-plugin';  
371 - case types.entityType.tenant:  
372 - return 'entity.type-tenant';  
373 - case types.entityType.customer:  
374 - return 'entity.type-customer';  
375 - case types.entityType.user:  
376 - return 'entity.type-user';  
377 - case types.entityType.dashboard:  
378 - return 'entity.type-dashboard';  
379 - case types.entityType.alarm:  
380 - return 'entity.type-alarm';  
381 - }  
382 - }  
383 -  
384 } 360 }
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 */ 15 */
16 /* eslint-disable import/no-unresolved, import/default */ 16 /* eslint-disable import/no-unresolved, import/default */
17 17
18 -import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; 18 +import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
19 19
20 /* eslint-enable import/no-unresolved, import/default */ 20 /* eslint-enable import/no-unresolved, import/default */
21 21
@@ -130,17 +130,14 @@ export default function AddWidgetController($scope, widgetService, entityService @@ -130,17 +130,14 @@ export default function AddWidgetController($scope, widgetService, entityService
130 var singleEntityAlias = {id: null, alias: alias, filter: {}}; 130 var singleEntityAlias = {id: null, alias: alias, filter: {}};
131 131
132 $mdDialog.show({ 132 $mdDialog.show({
133 - controller: 'EntityAliasesController', 133 + controller: 'EntityAliasDialogController',
134 controllerAs: 'vm', 134 controllerAs: 'vm',
135 - templateUrl: entityAliasesTemplate, 135 + templateUrl: entityAliasDialogTemplate,
136 locals: { 136 locals: {
137 - config: {  
138 - entityAliases: angular.copy(vm.dashboard.configuration.entityAliases),  
139 - widgets: null,  
140 - isSingleEntityAlias: true,  
141 - singleEntityAlias: singleEntityAlias,  
142 - allowedEntityTypes: allowedEntityTypes  
143 - } 137 + isAdd: true,
  138 + allowedEntityTypes: allowedEntityTypes,
  139 + entityAliases: vm.dashboard.configuration.entityAliases,
  140 + alias: singleEntityAlias
144 }, 141 },
145 parent: angular.element($document[0].body), 142 parent: angular.element($document[0].body),
146 fullscreen: true, 143 fullscreen: true,
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 */ 15 */
16 /* eslint-disable import/no-unresolved, import/default */ 16 /* eslint-disable import/no-unresolved, import/default */
17 17
18 -import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; 18 +import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
19 import editWidgetTemplate from './edit-widget.tpl.html'; 19 import editWidgetTemplate from './edit-widget.tpl.html';
20 20
21 /* eslint-enable import/no-unresolved, import/default */ 21 /* eslint-enable import/no-unresolved, import/default */
@@ -98,17 +98,14 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid @@ -98,17 +98,14 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
98 var singleEntityAlias = {id: null, alias: alias, filter: {}}; 98 var singleEntityAlias = {id: null, alias: alias, filter: {}};
99 99
100 $mdDialog.show({ 100 $mdDialog.show({
101 - controller: 'EntityAliasesController', 101 + controller: 'EntityAliasDialogController',
102 controllerAs: 'vm', 102 controllerAs: 'vm',
103 - templateUrl: entityAliasesTemplate, 103 + templateUrl: entityAliasDialogTemplate,
104 locals: { 104 locals: {
105 - config: {  
106 - entityAliases: angular.copy(scope.dashboard.configuration.entityAliases),  
107 - widgets: null,  
108 - isSingleEntityAlias: true,  
109 - singleEntityAlias: singleEntityAlias,  
110 - allowedEntityTypes: allowedEntityTypes  
111 - } 105 + isAdd: true,
  106 + allowedEntityTypes: allowedEntityTypes,
  107 + entityAliases: scope.dashboard.configuration.entityAliases,
  108 + alias: singleEntityAlias
112 }, 109 },
113 parent: angular.element($document[0].body), 110 parent: angular.element($document[0].body),
114 fullscreen: true, 111 fullscreen: true,
ui/src/app/entity/entity-alias-dialog.controller.js renamed from ui/src/app/entity/entity-filter-dialog.controller.js
@@ -14,22 +14,48 @@ @@ -14,22 +14,48 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
  17 +import './entity-alias-dialog.scss';
  18 +
17 /*@ngInject*/ 19 /*@ngInject*/
18 -export default function EntityFilterDialogController($scope, $mdDialog, $q, entityService, types, isAdd, allowedEntityTypes, filter) { 20 +export default function EntityAliasDialogController($scope, $mdDialog, $q, $filter, utils, entityService, types, isAdd, allowedEntityTypes, entityAliases, alias) {
19 21
20 var vm = this; 22 var vm = this;
21 23
22 vm.types = types; 24 vm.types = types;
23 vm.isAdd = isAdd; 25 vm.isAdd = isAdd;
24 vm.allowedEntityTypes = allowedEntityTypes; 26 vm.allowedEntityTypes = allowedEntityTypes;
25 - vm.filter = filter; 27 + if (angular.isArray(entityAliases)) {
  28 + vm.entityAliases = entityAliases;
  29 + } else {
  30 + vm.entityAliases = [];
  31 + for (var aliasId in entityAliases) {
  32 + vm.entityAliases.push(entityAliases[aliasId]);
  33 + }
  34 + }
  35 + if (vm.isAdd && !alias) {
  36 + vm.alias = {
  37 + alias: '',
  38 + filter: {
  39 + resolveMultiple: false
  40 + }
  41 + };
  42 + } else {
  43 + vm.alias = alias;
  44 + }
26 45
27 vm.cancel = cancel; 46 vm.cancel = cancel;
28 vm.save = save; 47 vm.save = save;
29 48
30 - $scope.$watch('vm.filter.type', function (newType, prevType) {  
31 - if (newType && newType != prevType) {  
32 - updateFilter(); 49 + $scope.$watch('vm.alias.alias', function (newAlias) {
  50 + if (newAlias) {
  51 + var valid = true;
  52 + var result = $filter('filter')(vm.entityAliases, {alias: newAlias}, true);
  53 + if (result && result.length) {
  54 + if (vm.isAdd || vm.alias.id != result[0].id) {
  55 + valid = false;
  56 + }
  57 + }
  58 + $scope.theForm.aliasName.$setValidity('duplicateAliasName', valid);
33 } 59 }
34 }); 60 });
35 61
@@ -39,32 +65,13 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti @@ -39,32 +65,13 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
39 } 65 }
40 }); 66 });
41 67
42 - function updateFilter() {  
43 - var filter = {};  
44 - filter.type = vm.filter.type;  
45 - filter.resolveMultiple = vm.filter.resolveMultiple;  
46 - switch (filter.type) {  
47 - case types.aliasFilterType.entityList.value:  
48 - filter.entityType = null;  
49 - filter.entityList = [];  
50 - filter.stateEntity = false;  
51 - break;  
52 - case types.aliasFilterType.entityName.value:  
53 - filter.entityType = null;  
54 - filter.entityNameFilter = '';  
55 - break;  
56 - //TODO:  
57 - }  
58 - vm.filter = filter;  
59 - }  
60 -  
61 function validate() { 68 function validate() {
62 var deferred = $q.defer(); 69 var deferred = $q.defer();
63 var validationResult = { 70 var validationResult = {
64 entity: null, 71 entity: null,
65 stateEntity: false 72 stateEntity: false
66 } 73 }
67 - entityService.resolveAliasFilter(vm.filter, null, 1).then( 74 + entityService.resolveAliasFilter(vm.alias.filter, null, 1).then(
68 function success(result) { 75 function success(result) {
69 validationResult.stateEntity = result.stateEntity; 76 validationResult.stateEntity = result.stateEntity;
70 var entities = result.entities; 77 var entities = result.entities;
@@ -87,12 +94,11 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti @@ -87,12 +94,11 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
87 function save() { 94 function save() {
88 $scope.theForm.$setPristine(); 95 $scope.theForm.$setPristine();
89 validate().then( 96 validate().then(
90 - function success(validationResult) {  
91 - $mdDialog.hide({  
92 - filter: vm.filter,  
93 - entity: validationResult.entity,  
94 - stateEntity: validationResult.stateEntity  
95 - }); 97 + function success() {
  98 + if (vm.isAdd) {
  99 + vm.alias.id = utils.guid();
  100 + }
  101 + $mdDialog.hide(vm.alias);
96 }, 102 },
97 function fail() { 103 function fail() {
98 $scope.theForm.$setValidity('entityFilter', false); 104 $scope.theForm.$setValidity('entityFilter', false);
@@ -101,4 +107,3 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti @@ -101,4 +107,3 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
101 } 107 }
102 108
103 } 109 }
104 -  
  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 +
  17 +.tb-entity-alias-dialog {
  18 + .tb-resolve-multiple-switch {
  19 + padding-left: 10px;
  20 + .resolve-multiple-switch {
  21 + margin: 0;
  22 + }
  23 + .resolve-multiple-label {
  24 + margin: 5px 0;
  25 + }
  26 + }
  27 +}
ui/src/app/entity/entity-alias-dialog.tpl.html renamed from ui/src/app/entity/entity-filter-dialog.tpl.html
@@ -15,11 +15,11 @@ @@ -15,11 +15,11 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<md-dialog class="tb-entity-filter-dialog" style="width: 600px;" aria-label="{{ 'alias.entity-filter' | translate }}"> 18 +<md-dialog class="tb-entity-alias-dialog" style="width: 600px;" aria-label="{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}">
19 <form name="theForm" ng-submit="vm.save()"> 19 <form name="theForm" ng-submit="vm.save()">
20 <md-toolbar> 20 <md-toolbar>
21 <div class="md-toolbar-tools"> 21 <div class="md-toolbar-tools">
22 - <h2>{{ (vm.isAdd ? 'alias.create-entity-filter' : 'alias.edit-entity-filter') | translate }}</h2> 22 + <h2>{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2>
23 <span flex></span> 23 <span flex></span>
24 <md-button class="md-icon-button" ng-click="vm.cancel()"> 24 <md-button class="md-icon-button" ng-click="vm.cancel()">
25 <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> 25 <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
@@ -32,56 +32,29 @@ @@ -32,56 +32,29 @@
32 <div class="md-dialog-content"> 32 <div class="md-dialog-content">
33 <fieldset ng-disabled="loading"> 33 <fieldset ng-disabled="loading">
34 <div flex layout="column"> 34 <div flex layout="column">
35 - <md-input-container>  
36 - <label>{{ 'alias.filter-type' | translate }}</label>  
37 - <md-select required name="filterType"  
38 - ng-model="vm.filter.type" aria-label="{{ 'alias.filter-type' | translate }}">  
39 - <md-option ng-repeat="type in vm.types.aliasFilterType" ng-value="type.value">  
40 - {{type.name | translate}}  
41 - </md-option>  
42 - </md-select>  
43 - <div ng-messages="theForm.filterType.$error">  
44 - <div ng-message="required" translate>alias.filter-type-required</div>  
45 - </div>  
46 - </md-input-container>  
47 - <section layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityList.value" id="entityListFilter">  
48 - <md-checkbox flex aria-label="{{ 'alias.use-state-entity' | translate }}"  
49 - ng-model="vm.filter.stateEntity">{{ 'alias.use-state-entity' | translate }}  
50 - </md-checkbox>  
51 - <tb-entity-type-select  
52 - ng-if="!vm.filter.stateEntity"  
53 - ng-model="vm.filter.entityType"  
54 - the-form="theForm"  
55 - ng-disabled="vm.filter.stateEntity"  
56 - tb-required="!vm.filter.stateEntity"  
57 - allowed-entity-types="vm.allowedEntityTypes">  
58 - </tb-entity-type-select>  
59 - <tb-entity-list  
60 - ng-if="!vm.filter.stateEntity"  
61 - ng-model="vm.filter.entityList"  
62 - ng-disabled="vm.filter.stateEntity"  
63 - tb-required="!vm.filter.stateEntity"  
64 - entity-type="vm.filter.entityType">  
65 - </tb-entity-list>  
66 - </section>  
67 - <section flex layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityName.value" id="entityNameFilter">  
68 - <tb-entity-type-select  
69 - ng-model="vm.filter.entityType"  
70 - the-form="theForm"  
71 - tb-required="true"  
72 - allowed-entity-types="vm.allowedEntityTypes">  
73 - </tb-entity-type-select>  
74 - <md-input-container flex>  
75 - <label translate>entity.name-starts-with</label>  
76 - <input required name="entityNameFilter"  
77 - ng-model="vm.filter.entityNameFilter"  
78 - aria-label="{{ 'entity.name-starts-with' | translate }}">  
79 - <div ng-messages="theForm.entityNameFilter.$error">  
80 - <div ng-message="required" translate>entity.entity-name-filter-required</div> 35 + <div layout="row">
  36 + <md-input-container flex class="md-block">
  37 + <label translate>alias.name</label>
  38 + <input required name="aliasName"
  39 + ng-model="vm.alias.alias"
  40 + aria-label="{{ 'alias.name' | translate }}">
  41 + <div ng-messages="theForm.aliasName.$error">
  42 + <div ng-message="required" translate>alias.name-required</div>
  43 + <div ng-message="duplicateAliasName" translate>alias.duplicate-alias</div>
81 </div> 44 </div>
82 -  
83 </md-input-container> 45 </md-input-container>
84 - </section> 46 + <section class="tb-resolve-multiple-switch" layout="column" layout-align="start center">
  47 + <label class="tb-small resolve-multiple-label" translate>alias.resolve-multiple</label>
  48 + <md-switch class="resolve-multiple-switch" ng-model="vm.alias.filter.resolveMultiple"
  49 + aria-label="{{ 'alias.resolve-multiple' | translate }}">
  50 + </md-switch>
  51 + </section>
  52 + </div>
  53 + <tb-entity-filter
  54 + ng-model="vm.alias.filter"
  55 + allowed-entity-types="vm.allowedEntityTypes"
  56 + the-form="theForm">
  57 + </tb-entity-filter>
85 <div class="tb-error-messages" ng-messages="theForm.$error" role="alert"> 58 <div class="tb-error-messages" ng-messages="theForm.$error" role="alert">
86 <div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div> 59 <div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div>
87 </div> 60 </div>
@@ -92,9 +65,9 @@ @@ -92,9 +65,9 @@
92 <md-dialog-actions layout="row"> 65 <md-dialog-actions layout="row">
93 <span flex></span> 66 <span flex></span>
94 <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> 67 <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
95 - {{ 'action.save' | translate }} 68 + {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
96 </md-button> 69 </md-button>
97 <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> 70 <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
98 </md-dialog-actions> 71 </md-dialog-actions>
99 </form> 72 </form>
100 -</md-dialog>  
  73 +</md-dialog>
@@ -15,22 +15,27 @@ @@ -15,22 +15,27 @@
15 */ 15 */
16 import './entity-aliases.scss'; 16 import './entity-aliases.scss';
17 17
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import entityAliasDialogTemplate from './entity-alias-dialog.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
18 /*@ngInject*/ 24 /*@ngInject*/
19 export default function EntityAliasesController(utils, entityService, toast, $scope, $mdDialog, $document, $q, $translate, 25 export default function EntityAliasesController(utils, entityService, toast, $scope, $mdDialog, $document, $q, $translate,
20 types, config) { 26 types, config) {
21 27
22 var vm = this; 28 var vm = this;
23 29
24 - vm.isSingleEntityAlias = config.isSingleEntityAlias;  
25 - vm.singleEntityAlias = config.singleEntityAlias; 30 + vm.types = types;
26 vm.entityAliases = []; 31 vm.entityAliases = [];
27 vm.title = config.customTitle ? config.customTitle : 'entity.aliases'; 32 vm.title = config.customTitle ? config.customTitle : 'entity.aliases';
28 vm.disableAdd = config.disableAdd; 33 vm.disableAdd = config.disableAdd;
29 vm.aliasToWidgetsMap = {}; 34 vm.aliasToWidgetsMap = {};
30 vm.allowedEntityTypes = config.allowedEntityTypes; 35 vm.allowedEntityTypes = config.allowedEntityTypes;
31 36
32 - vm.onFilterEntityChanged = onFilterEntityChanged;  
33 vm.addAlias = addAlias; 37 vm.addAlias = addAlias;
  38 + vm.editAlias = editAlias;
34 vm.removeAlias = removeAlias; 39 vm.removeAlias = removeAlias;
35 40
36 vm.cancel = cancel; 41 vm.cancel = cancel;
@@ -79,42 +84,61 @@ export default function EntityAliasesController(utils, entityService, toast, $sc @@ -79,42 +84,61 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
79 } 84 }
80 } 85 }
81 86
82 - if (vm.isSingleEntityAlias) {  
83 - checkEntityAlias(vm.singleEntityAlias);  
84 - }  
85 -  
86 for (aliasId in config.entityAliases) { 87 for (aliasId in config.entityAliases) {
87 var entityAlias = config.entityAliases[aliasId]; 88 var entityAlias = config.entityAliases[aliasId];
88 - var result = {id: aliasId, alias: entityAlias.alias, filter: entityAlias.filter, changed: true};  
89 - checkEntityAlias(result); 89 + var filter = entityAlias.filter;
  90 + if (!filter) {
  91 + filter = {
  92 + resolveMultiple: false
  93 + };
  94 + }
  95 + if (!filter.resolveMultiple) {
  96 + filter.resolveMultiple = false;
  97 + }
  98 + var result = {id: aliasId, alias: entityAlias.alias, filter: filter};
90 vm.entityAliases.push(result); 99 vm.entityAliases.push(result);
91 } 100 }
92 } 101 }
93 102
94 - function checkEntityAlias(entityAlias) {  
95 - if (!entityAlias.filter || entityAlias.filter == null) {  
96 - entityAlias.filter = {};  
97 - } 103 + function addAlias($event) {
  104 + openAliasDialog($event);
98 } 105 }
99 106
100 - function onFilterEntityChanged(entity, stateEntity, entityAlias) {  
101 - if (entityAlias) {  
102 - if (!entityAlias.alias || entityAlias.alias.length == 0) {  
103 - entityAlias.changed = false;  
104 - }  
105 - if (!entityAlias.changed && entityAlias.filter && entityAlias.filter.type) {  
106 - if (stateEntity) {  
107 - entityAlias.alias = $translate.instant('alias.state-entity');  
108 - } else {  
109 - entityAlias.alias = entity.name;  
110 - }  
111 - }  
112 - } 107 + function editAlias($event, entityAlias) {
  108 + openAliasDialog($event, entityAlias);
113 } 109 }
114 110
115 - function addAlias() {  
116 - var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false};  
117 - vm.entityAliases.push(entityAlias); 111 + function openAliasDialog($event, entityAlias) {
  112 + var isAdd = entityAlias ? false : true;
  113 + var aliasIndex;
  114 + if (!isAdd) {
  115 + aliasIndex = vm.entityAliases.indexOf(entityAlias);
  116 + }
  117 + $mdDialog.show({
  118 + controller: 'EntityAliasDialogController',
  119 + controllerAs: 'vm',
  120 + templateUrl: entityAliasDialogTemplate,
  121 + locals: {
  122 + isAdd: isAdd,
  123 + allowedEntityTypes: vm.allowedEntityTypes,
  124 + entityAliases: vm.entityAliases,
  125 + alias: isAdd ? null : angular.copy(entityAlias)
  126 + },
  127 + parent: angular.element($document[0].body),
  128 + fullscreen: true,
  129 + skipHide: true,
  130 + targetEvent: $event
  131 + }).then(function (alias) {
  132 + if (isAdd) {
  133 + vm.entityAliases.push(alias);
  134 + } else {
  135 + vm.entityAliases[aliasIndex] = alias;
  136 + }
  137 + if ($scope.theForm) {
  138 + $scope.theForm.$setDirty();
  139 + }
  140 + }, function () {
  141 + });
118 } 142 }
119 143
120 function removeAlias($event, entityAlias) { 144 function removeAlias($event, entityAlias) {
@@ -157,43 +181,30 @@ export default function EntityAliasesController(utils, entityService, toast, $sc @@ -157,43 +181,30 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
157 var uniqueAliasList = {}; 181 var uniqueAliasList = {};
158 182
159 var valid = true; 183 var valid = true;
160 - var aliasId;  
161 - var alias;  
162 - var i;  
163 -  
164 - if (vm.isSingleEntityAlias) {  
165 - if (!vm.singleEntityAlias.id) {  
166 - vm.singleEntityAlias.id = utils.guid();  
167 - }  
168 - for (i = 0; i < vm.entityAliases.length; i ++) {  
169 - alias = vm.entityAliases[i].alias;  
170 - if (alias === vm.singleEntityAlias.alias) {  
171 - valid = false;  
172 - break;  
173 - }  
174 - }  
175 - } else {  
176 - for (i = 0; i < vm.entityAliases.length; i++) {  
177 - aliasId = vm.entityAliases[i].id;  
178 - alias = vm.entityAliases[i].alias;  
179 - if (!uniqueAliasList[alias]) {  
180 - uniqueAliasList[alias] = alias;  
181 - entityAliases[aliasId] = {id: aliasId, alias: alias, filter: vm.entityAliases[i].filter};  
182 - } else {  
183 - valid = false;  
184 - break;  
185 - } 184 + var message, aliasId, alias, filter;
  185 +
  186 + for (var i = 0; i < vm.entityAliases.length; i++) {
  187 + aliasId = vm.entityAliases[i].id;
  188 + alias = vm.entityAliases[i].alias;
  189 + filter = vm.entityAliases[i].filter;
  190 + if (uniqueAliasList[alias]) {
  191 + valid = false;
  192 + message = $translate.instant('entity.duplicate-alias-error', {alias: alias});
  193 + break;
  194 + } else if (!filter || !filter.type) {
  195 + valid = false;
  196 + message = $translate.instant('entity.missing-entity-filter-error', {alias: alias});
  197 + break;
  198 + } else {
  199 + uniqueAliasList[alias] = alias;
  200 + entityAliases[aliasId] = {id: aliasId, alias: alias, filter: filter};
186 } 201 }
187 } 202 }
188 if (valid) { 203 if (valid) {
189 $scope.theForm.$setPristine(); 204 $scope.theForm.$setPristine();
190 - if (vm.isSingleEntityAlias) {  
191 - $mdDialog.hide(vm.singleEntityAlias);  
192 - } else {  
193 - $mdDialog.hide(entityAliases);  
194 - } 205 + $mdDialog.hide(entityAliases);
195 } else { 206 } else {
196 - toast.showError($translate.instant('entity.duplicate-alias-error', {alias: alias})); 207 + toast.showError(message);
197 } 208 }
198 } 209 }
199 210
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 .tb-aliases-dialog { 17 .tb-aliases-dialog {
18 .md-dialog-content { 18 .md-dialog-content {
19 padding-bottom: 0px; 19 padding-bottom: 0px;
  20 + padding-top: 0px;
20 } 21 }
21 .tb-aliases-header { 22 .tb-aliases-header {
22 min-height: 40px; 23 min-height: 40px;
@@ -33,5 +34,16 @@ @@ -33,5 +34,16 @@
33 md-input-container { 34 md-input-container {
34 margin: 0px; 35 margin: 0px;
35 } 36 }
  37 + .tb-resolve-multiple-switch {
  38 + padding-left: 10px;
  39 + .resolve-multiple-switch {
  40 + margin: 0;
  41 + }
  42 + }
  43 + .md-button {
  44 + &.md-icon-button {
  45 + margin: 0px;
  46 + }
  47 + }
36 } 48 }
37 } 49 }
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 <form name="theForm" ng-submit="vm.save()"> 19 <form name="theForm" ng-submit="vm.save()">
20 <md-toolbar> 20 <md-toolbar>
21 <div class="md-toolbar-tools"> 21 <div class="md-toolbar-tools">
22 - <h2>{{ vm.isSingleEntityAlias ? ('entity.configure-alias' | translate:vm.singleEntityAlias ) : (vm.title | translate) }}</h2> 22 + <h2>{{ vm.title | translate }}</h2>
23 <span flex></span> 23 <span flex></span>
24 <md-button class="md-icon-button" ng-click="vm.cancel()"> 24 <md-button class="md-icon-button" ng-click="vm.cancel()">
25 <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> 25 <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
@@ -28,66 +28,72 @@ @@ -28,66 +28,72 @@
28 </md-toolbar> 28 </md-toolbar>
29 <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> 29 <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30 <span style="min-height: 5px;" flex="" ng-show="!loading"></span> 30 <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
31 - <div class="tb-aliases-header" ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> 31 + <div class="tb-aliases-header" flex layout="row" layout-align="start center">
32 <span flex="5"></span> 32 <span flex="5"></span>
33 <div flex layout="row" layout-align="start center"> 33 <div flex layout="row" layout-align="start center">
34 - <span class="tb-header-label" translate flex="20" style="min-width: 150px;">entity.alias</span>  
35 - <div flex="80" layout="row" layout-align="start center" style="min-width: 240px; padding-left: 10px;">  
36 - <span class="tb-header-label" translate flex="70">alias.entity-filter</span>  
37 - <span class="tb-header-label" translate flex="30" style="padding-left: 10px;" >alias.resolve-multiple</span>  
38 - </div>  
39 - <span style="min-width: 40px; margin: 0 6px;"></span> 34 + <span class="tb-header-label" translate flex="20" style="min-width: 150px;">alias.name</span>
  35 + <span class="tb-header-label" translate flex="70" style="padding-left: 10px;">alias.entity-filter</span>
  36 + <span class="tb-header-label" translate flex="10" style="padding-left: 10px; min-width: 120px;">alias.resolve-multiple</span>
  37 + <span style="min-width: 80px;"></span>
40 </div> 38 </div>
41 </div> 39 </div>
42 - <md-divider ng-show="!vm.isSingleEntityAlias"></md-divider> 40 + <md-divider></md-divider>
43 <md-dialog-content> 41 <md-dialog-content>
44 <div class="md-dialog-content"> 42 <div class="md-dialog-content">
45 <fieldset ng-disabled="loading"> 43 <fieldset ng-disabled="loading">
46 - <div ng-show="vm.isSingleEntityAlias" layout="row">  
47 - <tb-entity-filter flex  
48 - allowed-entity-types="vm.allowedEntityTypes"  
49 - ng-model="vm.singleEntityAlias.filter">  
50 - </tb-entity-filter>  
51 - </div>  
52 - <div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;">  
53 - <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">  
54 - <span flex="5">{{$index + 1}}.</span>  
55 - <div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">  
56 - <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">  
57 - <input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">  
58 - <div ng-messages="aliasForm.alias.$error">  
59 - <div translate ng-message="required">entity.alias-required</div>  
60 - </div>  
61 - </md-input-container>  
62 - <tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;"  
63 - hide-labels  
64 - allowed-entity-types="vm.allowedEntityTypes"  
65 - ng-model="entityAlias.filter"  
66 - on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">  
67 - </tb-entity-filter>  
68 - <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"  
69 - ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">  
70 - <md-tooltip md-direction="top">  
71 - {{ 'entity.remove-alias' | translate }}  
72 - </md-tooltip>  
73 - <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">  
74 - close  
75 - </md-icon>  
76 - </md-button>  
77 - </div>  
78 - </div> 44 + <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
  45 + <span flex="5">{{$index + 1}}.</span>
  46 + <di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
  47 + <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
  48 + <input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
  49 + <div ng-messages="aliasForm.alias.$error">
  50 + <div translate ng-message="required">entity.alias-required</div>
  51 + </div>
  52 + </md-input-container>
  53 + <tb-entity-filter-view
  54 + flex="70" style="padding-left: 10px;"
  55 + ng-model="entityAlias.filter">
  56 + </tb-entity-filter-view>
  57 + <section flex="10" style="padding-left: 10px; min-width: 120px;"
  58 + class="tb-resolve-multiple-switch"
  59 + layout="column"
  60 + layout-align="center center">
  61 + <md-switch class="resolve-multiple-switch"
  62 + ng-model="entityAlias.filter.resolveMultiple"
  63 + aria-label="resolve-multiple-switcher">
  64 + </md-switch>
  65 + </section>
  66 + <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
  67 + ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
  68 + <md-tooltip md-direction="top">
  69 + {{ 'alias.edit' | translate }}
  70 + </md-tooltip>
  71 + <md-icon aria-label="{{ 'alias.edit' | translate }}" class="material-icons">
  72 + edit
  73 + </md-icon>
  74 + </md-button>
  75 + <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
  76 + ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
  77 + <md-tooltip md-direction="top">
  78 + {{ 'entity.remove-alias' | translate }}
  79 + </md-tooltip>
  80 + <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">
  81 + close
  82 + </md-icon>
  83 + </md-button>
  84 + </di>
79 </div> 85 </div>
80 </fieldset> 86 </fieldset>
81 </div> 87 </div>
82 </md-dialog-content> 88 </md-dialog-content>
83 <md-dialog-actions layout="row"> 89 <md-dialog-actions layout="row">
84 - <md-button ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" 90 + <md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
85 ng-click="vm.addAlias($event)" 91 ng-click="vm.addAlias($event)"
86 - aria-label="{{ 'action.add' | translate }}"> 92 + aria-label="{{ 'alias.add' | translate }}">
87 <md-tooltip md-direction="top"> 93 <md-tooltip md-direction="top">
88 - {{ 'entity.add-alias' | translate }} 94 + {{ 'alias.add' | translate }}
89 </md-tooltip> 95 </md-tooltip>
90 - <span translate>action.add</span> 96 + <span translate>alias.add</span>
91 </md-button> 97 </md-button>
92 <span flex></span> 98 <span flex></span>
93 <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> 99 <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  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 +
  17 +/* eslint-disable import/no-unresolved, import/default */
  18 +
  19 +import entityFilterViewTemplate from './entity-filter-view.tpl.html';
  20 +
  21 +/* eslint-enable import/no-unresolved, import/default */
  22 +
  23 +import './entity-filter-view.scss';
  24 +
  25 +/*@ngInject*/
  26 +export default function EntityFilterViewDirective($compile, $templateCache, $q, $document, $mdDialog, $translate, types/*, entityService*/) {
  27 +
  28 + var linker = function (scope, element, attrs, ngModelCtrl) {
  29 +
  30 + var template = $templateCache.get(entityFilterViewTemplate);
  31 + element.html(template);
  32 +
  33 + scope.ngModelCtrl = ngModelCtrl;
  34 + scope.types = types;
  35 + scope.filterDisplayValue = '';
  36 +
  37 + scope.$watch('filter', function () {
  38 + scope.updateDisplayValue();
  39 + });
  40 +
  41 + scope.updateDisplayValue = function() {
  42 + if (scope.filter && scope.filter.type) {
  43 + var entityType;
  44 + var prefix;
  45 + switch (scope.filter.type) {
  46 + case types.aliasFilterType.entityList.value:
  47 + entityType = scope.filter.entityType;
  48 + var count = scope.filter.entityList.length;
  49 + scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].list, {count: count}, 'messageformat');
  50 + break;
  51 + case types.aliasFilterType.entityName.value:
  52 + entityType = scope.filter.entityType;
  53 + prefix = scope.filter.entityNameFilter;
  54 + scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].nameStartsWith, {prefix: prefix});
  55 + break;
  56 + case types.aliasFilterType.stateEntity.value:
  57 + scope.filterDisplayValue = $translate.instant('alias.filter-type-state-entity-description');
  58 + break;
  59 + case types.aliasFilterType.assetType.value:
  60 + var assetType = scope.filter.assetType;
  61 + prefix = scope.filter.assetNameFilter;
  62 + if (prefix && prefix.length) {
  63 + scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-and-name-description', {assetType: assetType, prefix: prefix});
  64 + } else {
  65 + scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-description', {assetType: assetType});
  66 + }
  67 + break;
  68 + case types.aliasFilterType.deviceType.value:
  69 + var deviceType = scope.filter.deviceType;
  70 + prefix = scope.filter.deviceNameFilter;
  71 + if (prefix && prefix.length) {
  72 + scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-and-name-description', {deviceType: deviceType, prefix: prefix});
  73 + } else {
  74 + scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType});
  75 + }
  76 + break;
  77 + //TODO: Alias filter
  78 + default:
  79 + scope.filterDisplayValue = scope.filter.type;
  80 + break;
  81 + }
  82 + } else {
  83 + scope.filterDisplayValue = '';
  84 + }
  85 + }
  86 +
  87 + ngModelCtrl.$render = function () {
  88 + if (ngModelCtrl.$viewValue) {
  89 + scope.filter = ngModelCtrl.$viewValue;
  90 + } else {
  91 + scope.filter = null;
  92 + }
  93 + }
  94 +
  95 + $compile(element.contents())(scope);
  96 +
  97 + }
  98 +
  99 + return {
  100 + restrict: "E",
  101 + require: "^ngModel",
  102 + link: linker,
  103 + scope: true
  104 + };
  105 +
  106 +}
  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 +
  17 +.tb-entity-filter-view {
  18 + .entity-filter-empty {
  19 + color: rgba(221, 44, 0, 0.87);
  20 + font-size: 14px;
  21 + line-height: 16px;
  22 + }
  23 + .entity-filter-type {
  24 + font-size: 14px;
  25 + line-height: 16px;
  26 + color: rgba(0, 0, 0, 0.570588);
  27 + }
  28 + .entity-filter-value {
  29 + font-size: 14px;
  30 + line-height: 16px;
  31 + color: rgba(0, 0, 0, 0.570588);
  32 + }
  33 +}
  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 +
  19 +<div layout='column' class="tb-entity-filter-view">
  20 + <div ng-if="!filter || !filter.type" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div>
  21 + <div ng-if="filter && filter.type" layout="column">
  22 + <div class="entity-filter-value">{{ filterDisplayValue }}</div>
  23 + </div>
  24 +</div>
@@ -17,16 +17,13 @@ @@ -17,16 +17,13 @@
17 /* eslint-disable import/no-unresolved, import/default */ 17 /* eslint-disable import/no-unresolved, import/default */
18 18
19 import entityFilterTemplate from './entity-filter.tpl.html'; 19 import entityFilterTemplate from './entity-filter.tpl.html';
20 -import entityFilterDialogTemplate from './entity-filter-dialog.tpl.html';  
21 20
22 /* eslint-enable import/no-unresolved, import/default */ 21 /* eslint-enable import/no-unresolved, import/default */
23 22
24 -import EntityFilterDialogController from './entity-filter-dialog.controller';  
25 -  
26 import './entity-filter.scss'; 23 import './entity-filter.scss';
27 24
28 /*@ngInject*/ 25 /*@ngInject*/
29 -export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types) { 26 +export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService) {
30 27
31 var linker = function (scope, element, attrs, ngModelCtrl) { 28 var linker = function (scope, element, attrs, ngModelCtrl) {
32 29
@@ -35,66 +32,59 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc @@ -35,66 +32,59 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
35 32
36 scope.ngModelCtrl = ngModelCtrl; 33 scope.ngModelCtrl = ngModelCtrl;
37 scope.types = types; 34 scope.types = types;
38 - scope.hideLabels = angular.isDefined(attrs.hideLabels); 35 + scope.aliasFilterTypes = entityService.getAliasFilterTypesByEntityTypes(scope.allowedEntityTypes);
39 36
40 - scope.updateValidity = function() {  
41 - if (ngModelCtrl.$viewValue) {  
42 - var value = ngModelCtrl.$viewValue;  
43 - ngModelCtrl.$setValidity('filter', value.type ? true : false); 37 + scope.$watch('filter.type', function (newType, prevType) {
  38 + if (newType && newType != prevType) {
  39 + updateFilter();
44 } 40 }
45 - } 41 + });
46 42
47 - ngModelCtrl.$render = function () {  
48 - if (ngModelCtrl.$viewValue) {  
49 - scope.model = angular.copy(ngModelCtrl.$viewValue);  
50 - } else {  
51 - scope.model = {  
52 - type: null,  
53 - resolveMultiple: false  
54 - } 43 + function updateFilter() {
  44 + var filter = {};
  45 + filter.type = scope.filter.type;
  46 + filter.resolveMultiple = scope.filter.resolveMultiple;
  47 + switch (filter.type) {
  48 + case types.aliasFilterType.entityList.value:
  49 + filter.entityType = null;
  50 + filter.entityList = [];
  51 + break;
  52 + case types.aliasFilterType.entityName.value:
  53 + filter.entityType = null;
  54 + filter.entityNameFilter = '';
  55 + break;
  56 + case types.aliasFilterType.stateEntity.value:
  57 + break;
  58 + case types.aliasFilterType.assetType.value:
  59 + filter.assetType = null;
  60 + filter.assetNameFilter = '';
  61 + break;
  62 + case types.aliasFilterType.deviceType.value:
  63 + filter.deviceType = null;
  64 + filter.deviceNameFilter = '';
  65 + break;
  66 + //TODO: Alias filter
55 } 67 }
  68 + scope.filter = filter;
56 } 69 }
57 70
58 - scope.$watch('model.resolveMultiple', function () {  
59 - if (ngModelCtrl.$viewValue) {  
60 - var value = ngModelCtrl.$viewValue;  
61 - value.resolveMultiple = scope.model.resolveMultiple;  
62 - ngModelCtrl.$setViewValue(value);  
63 - scope.updateValidity();  
64 - } 71 + scope.$watch('filter', function () {
  72 + scope.updateView();
65 }); 73 });
66 74
67 - scope.editFilter = function($event) {  
68 - openEntityFilterDialog($event, false); 75 + scope.updateView = function() {
  76 + ngModelCtrl.$setViewValue(scope.filter);
69 } 77 }
70 78
71 - scope.createFilter = function($event) {  
72 - openEntityFilterDialog($event, true);  
73 - }  
74 -  
75 - function openEntityFilterDialog($event, isAdd) {  
76 - $mdDialog.show({  
77 - controller: EntityFilterDialogController,  
78 - controllerAs: 'vm',  
79 - templateUrl: entityFilterDialogTemplate,  
80 - locals: {  
81 - isAdd: isAdd,  
82 - allowedEntityTypes: scope.allowedEntityTypes,  
83 - filter: angular.copy(scope.model)  
84 - },  
85 - parent: angular.element($document[0].body),  
86 - fullscreen: true,  
87 - skipHide: true,  
88 - targetEvent: $event  
89 - }).then(function (result) {  
90 - scope.model = result.filter;  
91 - ngModelCtrl.$setViewValue(result.filter);  
92 - scope.updateValidity();  
93 - if (scope.onMatchingEntityChange) {  
94 - scope.onMatchingEntityChange({entity: result.entity, stateEntity: result.stateEntity}); 79 + ngModelCtrl.$render = function () {
  80 + if (ngModelCtrl.$viewValue) {
  81 + scope.filter = ngModelCtrl.$viewValue;
  82 + } else {
  83 + scope.filter = {
  84 + type: null,
  85 + resolveMultiple: false
95 } 86 }
96 - }, function () {  
97 - }); 87 + }
98 } 88 }
99 89
100 $compile(element.contents())(scope); 90 $compile(element.contents())(scope);
@@ -106,8 +96,8 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc @@ -106,8 +96,8 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
106 require: "^ngModel", 96 require: "^ngModel",
107 link: linker, 97 link: linker,
108 scope: { 98 scope: {
109 - allowedEntityTypes: '=?',  
110 - onMatchingEntityChange: '&' 99 + theForm: '=',
  100 + allowedEntityTypes: '=?'
111 } 101 }
112 }; 102 };
113 103
@@ -13,22 +13,7 @@ @@ -13,22 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
  16 +
16 .tb-entity-filter { 17 .tb-entity-filter {
17 - .tb-filter-switch {  
18 - padding-left: 10px;  
19 - .filter-switch {  
20 - margin: 0;  
21 - }  
22 - .filter-label {  
23 - margin: 5px 0;  
24 - }  
25 - }  
26 - .tb-error-messages {  
27 - margin-top: -11px;  
28 - height: 35px;  
29 - .tb-error-message {  
30 - padding-left: 8px;  
31 - padding-top: 14px;  
32 - }  
33 - } 18 +
34 } 19 }
@@ -15,37 +15,77 @@ @@ -15,37 +15,77 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<section layout='row' class="tb-entity-filter">  
19 - <section layout="row" flex="70">  
20 - <section flex layout="column" layout-align="center start">  
21 - <div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>  
22 - <md-button ng-if="!model.type"  
23 - ng-disabled="loading" class="md-primary"  
24 - ng-click="createFilter($event)"  
25 - aria-label="{{ 'alias.create-entity-filter' | translate }}">  
26 - <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"  
27 - class="material-icons">  
28 - add  
29 - </md-icon>  
30 - {{ 'alias.create-entity-filter' | translate }}  
31 - </md-button>  
32 - </section>  
33 - <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary"  
34 - style="min-width: 40px;"  
35 - ng-click="editFilter($event)"  
36 - aria-label="{{ 'alias.edit-entity-filter' | translate }}">  
37 - <md-tooltip md-direction="top">  
38 - {{ 'alias.edit-entity-filter' | translate }}  
39 - </md-tooltip>  
40 - <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}"  
41 - class="material-icons">  
42 - edit  
43 - </md-icon>  
44 - </md-button> 18 +<div layout='column' class="tb-entity-filter">
  19 + <md-input-container class="md-block">
  20 + <label>{{ 'alias.filter-type' | translate }}</label>
  21 + <md-select required name="filterType"
  22 + ng-model="filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
  23 + <md-option ng-repeat="type in aliasFilterTypes" ng-value="type.value">
  24 + {{type.name | translate}}
  25 + </md-option>
  26 + </md-select>
  27 + <div ng-messages="theForm.filterType.$error">
  28 + <div ng-message="required" translate>alias.filter-type-required</div>
  29 + </div>
  30 + </md-input-container>
  31 + <section layout="column" ng-if="filter.type == types.aliasFilterType.entityList.value" id="entityListFilter">
  32 + <tb-entity-type-select
  33 + ng-model="filter.entityType"
  34 + the-form="theForm"
  35 + tb-required="true"
  36 + allowed-entity-types="allowedEntityTypes">
  37 + </tb-entity-type-select>
  38 + <tb-entity-list
  39 + ng-model="filter.entityList"
  40 + tb-required="true"
  41 + entity-type="filter.entityType">
  42 + </tb-entity-list>
45 </section> 43 </section>
46 - <section class="tb-filter-switch" layout="column" flex="30" layout-align="center center">  
47 - <label ng-if="!hideLabels" class="tb-small filter-label" translate>alias.resolve-multiple</label>  
48 - <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher">  
49 - </md-switch> 44 + <section flex layout="column" ng-if="filter.type == types.aliasFilterType.entityName.value" id="entityNameFilter">
  45 + <tb-entity-type-select
  46 + ng-model="filter.entityType"
  47 + the-form="theForm"
  48 + tb-required="true"
  49 + allowed-entity-types="allowedEntityTypes">
  50 + </tb-entity-type-select>
  51 + <md-input-container class="md-block">
  52 + <label translate>entity.name-starts-with</label>
  53 + <input required name="entityNameFilter"
  54 + ng-model="filter.entityNameFilter"
  55 + aria-label="{{ 'entity.name-starts-with' | translate }}">
  56 + <div ng-messages="theForm.entityNameFilter.$error">
  57 + <div ng-message="required" translate>entity.entity-name-filter-required</div>
  58 + </div>
  59 + </md-input-container>
50 </section> 60 </section>
51 -</section> 61 + <section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
  62 + </section>
  63 + <section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
  64 + <tb-entity-subtype-autocomplete
  65 + tb-required="true"
  66 + the-form="theForm"
  67 + ng-model="filter.assetType"
  68 + entity-type="types.entityType.asset">
  69 + </tb-entity-subtype-autocomplete>
  70 + <md-input-container class="md-block">
  71 + <label translate>asset.name-starts-with</label>
  72 + <input name="assetNameFilter"
  73 + ng-model="filter.assetNameFilter"
  74 + aria-label="{{ 'asset.name-starts-with' | translate }}">
  75 + </md-input-container>
  76 + </section>
  77 + <section layout="column" ng-if="filter.type == types.aliasFilterType.deviceType.value" id="deviceTypeFilter">
  78 + <tb-entity-subtype-autocomplete
  79 + tb-required="true"
  80 + the-form="theForm"
  81 + ng-model="filter.deviceType"
  82 + entity-type="types.entityType.device">
  83 + </tb-entity-subtype-autocomplete>
  84 + <md-input-container class="md-block">
  85 + <label translate>device.name-starts-with</label>
  86 + <input name="deviceNameFilter"
  87 + ng-model="filter.deviceNameFilter"
  88 + aria-label="{{ 'device.name-starts-with' | translate }}">
  89 + </md-input-container>
  90 + </section>
  91 +</div>
@@ -71,7 +71,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe @@ -71,7 +71,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
71 } 71 }
72 72
73 scope.typeName = function(type) { 73 scope.typeName = function(type) {
74 - return utils.entityTypeName(type); 74 + return type ? types.entityTypeTranslations[type].type : '';
75 } 75 }
76 76
77 scope.updateValidity = function () { 77 scope.updateValidity = function () {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 16
17 import EntityAliasesController from './entity-aliases.controller'; 17 import EntityAliasesController from './entity-aliases.controller';
  18 +import EntityAliasDialogController from './entity-alias-dialog.controller';
18 import EntityTypeSelectDirective from './entity-type-select.directive'; 19 import EntityTypeSelectDirective from './entity-type-select.directive';
19 import EntitySubtypeSelectDirective from './entity-subtype-select.directive'; 20 import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
20 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive'; 21 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
@@ -22,6 +23,7 @@ import EntityAutocompleteDirective from './entity-autocomplete.directive'; @@ -22,6 +23,7 @@ import EntityAutocompleteDirective from './entity-autocomplete.directive';
22 import EntityListDirective from './entity-list.directive'; 23 import EntityListDirective from './entity-list.directive';
23 import EntitySelectDirective from './entity-select.directive'; 24 import EntitySelectDirective from './entity-select.directive';
24 import EntityFilterDirective from './entity-filter.directive'; 25 import EntityFilterDirective from './entity-filter.directive';
  26 +import EntityFilterViewDirective from './entity-filter-view.directive';
25 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; 27 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
26 import AliasesEntitySelectDirective from './aliases-entity-select.directive'; 28 import AliasesEntitySelectDirective from './aliases-entity-select.directive';
27 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller'; 29 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller';
@@ -32,6 +34,7 @@ import RelationTypeAutocompleteDirective from './relation/relation-type-autocomp @@ -32,6 +34,7 @@ import RelationTypeAutocompleteDirective from './relation/relation-type-autocomp
32 34
33 export default angular.module('thingsboard.entity', []) 35 export default angular.module('thingsboard.entity', [])
34 .controller('EntityAliasesController', EntityAliasesController) 36 .controller('EntityAliasesController', EntityAliasesController)
  37 + .controller('EntityAliasDialogController', EntityAliasDialogController)
35 .controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController) 38 .controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController)
36 .controller('AddAttributeDialogController', AddAttributeDialogController) 39 .controller('AddAttributeDialogController', AddAttributeDialogController)
37 .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController) 40 .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController)
@@ -42,6 +45,7 @@ export default angular.module('thingsboard.entity', []) @@ -42,6 +45,7 @@ export default angular.module('thingsboard.entity', [])
42 .directive('tbEntityList', EntityListDirective) 45 .directive('tbEntityList', EntityListDirective)
43 .directive('tbEntitySelect', EntitySelectDirective) 46 .directive('tbEntitySelect', EntitySelectDirective)
44 .directive('tbEntityFilter', EntityFilterDirective) 47 .directive('tbEntityFilter', EntityFilterDirective)
  48 + .directive('tbEntityFilterView', EntityFilterViewDirective)
45 .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) 49 .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
46 .directive('tbAttributeTable', AttributeTableDirective) 50 .directive('tbAttributeTable', AttributeTableDirective)
47 .directive('tbRelationTable', RelationTableDirective) 51 .directive('tbRelationTable', RelationTableDirective)
@@ -218,9 +218,9 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ @@ -218,9 +218,9 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
218 function success(allRelations) { 218 function success(allRelations) {
219 allRelations.forEach(function(relation) { 219 allRelations.forEach(function(relation) {
220 if (vm.direction == vm.types.entitySearchDirection.from) { 220 if (vm.direction == vm.types.entitySearchDirection.from) {
221 - relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); 221 + relation.toEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.to.entityType].type);
222 } else { 222 } else {
223 - relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType)); 223 + relation.fromEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.from.entityType].type);
224 } 224 }
225 }); 225 });
226 vm.allRelations = allRelations; 226 vm.allRelations = allRelations;
@@ -365,7 +365,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -365,7 +365,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
365 alias = aliasInfo.aliasName; 365 alias = aliasInfo.aliasName;
366 filter = { 366 filter = {
367 type: types.aliasFilterType.entityList.value, 367 type: types.aliasFilterType.entityList.value,
368 - stateEntity: false,  
369 entityType: types.entityType.device, 368 entityType: types.entityType.device,
370 entityList: [aliasInfo.deviceId], 369 entityList: [aliasInfo.deviceId],
371 resolveMultiple: false 370 resolveMultiple: false
@@ -378,7 +377,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -378,7 +377,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
378 resolveMultiple: false 377 resolveMultiple: false
379 } 378 }
380 if (filter.type == types.aliasFilterType.entityList.value) { 379 if (filter.type == types.aliasFilterType.entityList.value) {
381 - filter.stateEntity = false;  
382 filter.entityList = aliasInfo.deviceFilter.deviceList 380 filter.entityList = aliasInfo.deviceFilter.deviceList
383 } else { 381 } else {
384 filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter; 382 filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter;
@@ -391,7 +389,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -391,7 +389,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
391 resolveMultiple: false 389 resolveMultiple: false
392 } 390 }
393 if (filter.type == types.aliasFilterType.entityList.value) { 391 if (filter.type == types.aliasFilterType.entityList.value) {
394 - filter.stateEntity = false;  
395 filter.entityList = aliasInfo.entityFilter.entityList; 392 filter.entityList = aliasInfo.entityFilter.entityList;
396 } else { 393 } else {
397 filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter; 394 filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter;
@@ -662,8 +659,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -662,8 +659,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
662 entityAliases: missingEntityAliases, 659 entityAliases: missingEntityAliases,
663 widgets: widgets, 660 widgets: widgets,
664 isSingleWidget: isSingleWidget, 661 isSingleWidget: isSingleWidget,
665 - isSingleEntityAlias: false,  
666 - singleEntityAlias: null,  
667 customTitle: customTitle, 662 customTitle: customTitle,
668 disableAdd: true 663 disableAdd: true
669 } 664 }
@@ -113,23 +113,30 @@ export default angular.module('thingsboard.locale', []) @@ -113,23 +113,30 @@ export default angular.module('thingsboard.locale', [])
113 "alarm-required": "Alarm is required" 113 "alarm-required": "Alarm is required"
114 }, 114 },
115 "alias": { 115 "alias": {
  116 + "add": "Add alias",
  117 + "edit": "Edit alias",
  118 + "name": "Alias name",
  119 + "name-required": "Alias name is required",
  120 + "duplicate-alias": "Alias with same name is already exists.",
116 "filter-type-entity-list": "Entity list", 121 "filter-type-entity-list": "Entity list",
117 "filter-type-entity-name": "Entity name", 122 "filter-type-entity-name": "Entity name",
  123 + "filter-type-state-entity": "Entity from dashboard state",
  124 + "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
118 "filter-type-asset-type": "Asset type", 125 "filter-type-asset-type": "Asset type",
  126 + "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
  127 + "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
119 "filter-type-device-type": "Device type", 128 "filter-type-device-type": "Device type",
  129 + "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
  130 + "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
120 "filter-type-relations-query": "Relations query", 131 "filter-type-relations-query": "Relations query",
121 "filter-type-asset-search-query": "Asset search query", 132 "filter-type-asset-search-query": "Asset search query",
122 "filter-type-device-search-query": "Device search query", 133 "filter-type-device-search-query": "Device search query",
123 "entity-filter": "Entity filter", 134 "entity-filter": "Entity filter",
124 - "create-entity-filter": "Create entity filter",  
125 - "edit-entity-filter": "Edit entity filter",  
126 - "entity-filter-required": "Entity filter is required.",  
127 "resolve-multiple": "Resolve as multiple entities", 135 "resolve-multiple": "Resolve as multiple entities",
128 "filter-type": "Filter type", 136 "filter-type": "Filter type",
129 "filter-type-required": "Filter type is required.", 137 "filter-type-required": "Filter type is required.",
130 - "use-state-entity": "Use state entity",  
131 - "state-entity": "State entity",  
132 "entity-filter-no-entity-matched": "No entities matching specified filter were found.", 138 "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
  139 + "no-entity-filter-specified": "No entity filter specified"
133 }, 140 },
134 "asset": { 141 "asset": {
135 "asset": "Asset", 142 "asset": "Asset",
@@ -185,7 +192,8 @@ export default angular.module('thingsboard.locale', []) @@ -185,7 +192,8 @@ export default angular.module('thingsboard.locale', [])
185 "idCopiedMessage": "Asset Id has been copied to clipboard", 192 "idCopiedMessage": "Asset Id has been copied to clipboard",
186 "select-asset": "Select asset", 193 "select-asset": "Select asset",
187 "no-assets-matching": "No assets matching '{{entity}}' were found.", 194 "no-assets-matching": "No assets matching '{{entity}}' were found.",
188 - "asset-required": "Asset is required" 195 + "asset-required": "Asset is required",
  196 + "name-starts-with": "Asset name starts with"
189 }, 197 },
190 "attribute": { 198 "attribute": {
191 "attributes": "Attributes", 199 "attributes": "Attributes",
@@ -463,7 +471,7 @@ export default angular.module('thingsboard.locale', []) @@ -463,7 +471,7 @@ export default angular.module('thingsboard.locale', [])
463 "alias-required": "Device alias is required.", 471 "alias-required": "Device alias is required.",
464 "remove-alias": "Remove device alias", 472 "remove-alias": "Remove device alias",
465 "add-alias": "Add device alias", 473 "add-alias": "Add device alias",
466 - "name-starts-with": "Name starts with", 474 + "name-starts-with": "Device name starts with",
467 "device-list": "Device list", 475 "device-list": "Device list",
468 "use-device-name-filter": "Use filter", 476 "use-device-name-filter": "Use filter",
469 "device-list-empty": "No devices selected.", 477 "device-list-empty": "No devices selected.",
@@ -549,6 +557,7 @@ export default angular.module('thingsboard.locale', []) @@ -549,6 +557,7 @@ export default angular.module('thingsboard.locale', [])
549 "unable-delete-entity-alias-title": "Unable to delete entity alias", 557 "unable-delete-entity-alias-title": "Unable to delete entity alias",
550 "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", 558 "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
551 "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.", 559 "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
  560 + "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
552 "configure-alias": "Configure '{{alias}}' alias", 561 "configure-alias": "Configure '{{alias}}' alias",
553 "alias": "Alias", 562 "alias": "Alias",
554 "alias-required": "Entity alias is required.", 563 "alias-required": "Entity alias is required.",
@@ -562,24 +571,42 @@ export default angular.module('thingsboard.locale', []) @@ -562,24 +571,42 @@ export default angular.module('thingsboard.locale', [])
562 "entity-name-filter-required": "Entity name filter is required.", 571 "entity-name-filter-required": "Entity name filter is required.",
563 "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", 572 "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
564 "all-subtypes": "All", 573 "all-subtypes": "All",
  574 + "select-entities": "Select entities",
  575 + "no-aliases-found": "No aliases found.",
  576 + "no-alias-matching": "'{{alias}}' not found.",
  577 + "create-new-alias": "Create a new one!",
  578 + "no-keys-found": "No keys found.",
  579 + "no-key-matching": "'{{key}}' not found.",
  580 + "create-new-key": "Create a new one!",
565 "type": "Type", 581 "type": "Type",
566 "type-required": "Entity type is required.", 582 "type-required": "Entity type is required.",
567 "type-device": "Device", 583 "type-device": "Device",
  584 + "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
  585 + "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
568 "type-asset": "Asset", 586 "type-asset": "Asset",
  587 + "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
  588 + "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
569 "type-rule": "Rule", 589 "type-rule": "Rule",
  590 + "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
  591 + "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
570 "type-plugin": "Plugin", 592 "type-plugin": "Plugin",
  593 + "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
  594 + "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
571 "type-tenant": "Tenant", 595 "type-tenant": "Tenant",
  596 + "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
  597 + "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
572 "type-customer": "Customer", 598 "type-customer": "Customer",
  599 + "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
  600 + "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
573 "type-user": "User", 601 "type-user": "User",
  602 + "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
  603 + "user-name-starts-with": "Users whose names start with '{{prefix}}'",
574 "type-dashboard": "Dashboard", 604 "type-dashboard": "Dashboard",
  605 + "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
  606 + "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
575 "type-alarm": "Alarm", 607 "type-alarm": "Alarm",
576 - "select-entities": "Select entities",  
577 - "no-aliases-found": "No aliases found.",  
578 - "no-alias-matching": "'{{alias}}' not found.",  
579 - "create-new-alias": "Create a new one!",  
580 - "no-keys-found": "No keys found.",  
581 - "no-key-matching": "'{{key}}' not found.",  
582 - "create-new-key": "Create a new one!" 608 + "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
  609 + "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'"
583 }, 610 },
584 "event": { 611 "event": {
585 "event-type": "Event type", 612 "event-type": "Event type",