Browse Source

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

pull/164/head
Igor Kulikov 9 years ago
parent
commit
9ef646765e
  1. 3
      ui/src/app/api/alias-controller.js
  2. 245
      ui/src/app/api/entity.service.js
  3. 6
      ui/src/app/api/subscription.js
  4. 4
      ui/src/app/common/dashboard-utils.service.js
  5. 51
      ui/src/app/common/types.constant.js
  6. 26
      ui/src/app/common/utils.service.js
  7. 17
      ui/src/app/dashboard/add-widget.controller.js
  8. 17
      ui/src/app/dashboard/edit-widget.directive.js
  9. 69
      ui/src/app/entity/entity-alias-dialog.controller.js
  10. 27
      ui/src/app/entity/entity-alias-dialog.scss
  11. 73
      ui/src/app/entity/entity-alias-dialog.tpl.html
  12. 133
      ui/src/app/entity/entity-aliases.controller.js
  13. 12
      ui/src/app/entity/entity-aliases.scss
  14. 98
      ui/src/app/entity/entity-aliases.tpl.html
  15. 100
      ui/src/app/entity/entity-filter-dialog.tpl.html
  16. 106
      ui/src/app/entity/entity-filter-view.directive.js
  17. 33
      ui/src/app/entity/entity-filter-view.scss
  18. 24
      ui/src/app/entity/entity-filter-view.tpl.html
  19. 100
      ui/src/app/entity/entity-filter.directive.js
  20. 19
      ui/src/app/entity/entity-filter.scss
  21. 104
      ui/src/app/entity/entity-filter.tpl.html
  22. 2
      ui/src/app/entity/entity-type-select.directive.js
  23. 4
      ui/src/app/entity/index.js
  24. 4
      ui/src/app/entity/relation/relation-table.directive.js
  25. 5
      ui/src/app/import-export/import-export.service.js
  26. 55
      ui/src/app/locale/locale.constant.js

3
ui/src/app/api/alias-controller.js

@ -95,8 +95,7 @@ export default class AliasController {
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
function success(aliasInfo) { function success(aliasInfo) {
aliasCtrl.resolvedAliases[aliasId] = aliasInfo; aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
if (entityAlias.filter.stateEntity) { if (aliasInfo.stateEntity) {
aliasInfo.stateEntity = true;
aliasCtrl.resolvedAliasesToStateEntities[aliasId] = aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
aliasCtrl.stateController.getStateParams().entityId; aliasCtrl.stateController.getStateParams().entityId;
} }

245
ui/src/app/api/entity.service.js

@ -31,6 +31,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
resolveAliasFilter: resolveAliasFilter, resolveAliasFilter: resolveAliasFilter,
checkEntityAlias: checkEntityAlias, checkEntityAlias: checkEntityAlias,
filterAliasByEntityTypes: filterAliasByEntityTypes, filterAliasByEntityTypes: filterAliasByEntityTypes,
getAliasFilterTypesByEntityTypes: getAliasFilterTypesByEntityTypes,
getEntityKeys: getEntityKeys, getEntityKeys: getEntityKeys,
createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo, createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
getRelatedEntities: getRelatedEntities, getRelatedEntities: getRelatedEntities,
@ -223,17 +224,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return promise; return promise;
} }
function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) { function getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred) {
var deferred = $q.defer();
var pageLink = {limit: limit, textSearch: entityNameFilter};
var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
if (promise) { if (promise) {
promise.then( promise.then(
function success(result) { function success(result) {
if (result.data && result.data.length > 0) { data = data.concat(result.data);
deferred.resolve(result.data); if (result.hasNext) {
pageLink = result.nextPageLink;
getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
} else { } else {
deferred.resolve(null); if (data && data.length > 0) {
deferred.resolve(data);
} else {
deferred.resolve(null);
}
} }
}, },
function fail() { function fail() {
@ -243,6 +248,34 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} else { } else {
deferred.resolve(null); deferred.resolve(null);
} }
}
function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
var deferred = $q.defer();
var pageLink = {limit: limit, textSearch: entityNameFilter};
if (limit == -1) { // all
var data = [];
pageLink.limit = 100;
getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
} else {
var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
if (promise) {
promise.then(
function success(result) {
if (result.data && result.data.length > 0) {
deferred.resolve(result.data);
} else {
deferred.resolve(null);
}
},
function fail() {
deferred.resolve(null);
}
);
} else {
deferred.resolve(null);
}
}
return deferred.promise; return deferred.promise;
} }
@ -261,11 +294,12 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
function resolveAlias(entityAlias, stateParams) { function resolveAlias(entityAlias, stateParams) {
var deferred = $q.defer(); var deferred = $q.defer();
var filter = entityAlias.filter; var filter = entityAlias.filter;
resolveAliasFilter(filter, stateParams, 100).then( resolveAliasFilter(filter, stateParams, -1).then(
function (result) { function (result) {
var entities = result.entities; var entities = result.entities;
var aliasInfo = { var aliasInfo = {
alias: entityAlias.alias, alias: entityAlias.alias,
stateEntity: result.stateEntity,
resolveMultiple: filter.resolveMultiple resolveMultiple: filter.resolveMultiple
}; };
var resolvedEntities = entitiesToEntitiesInfo(entities); var resolvedEntities = entitiesToEntitiesInfo(entities);
@ -291,39 +325,68 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
}; };
switch (filter.type) { switch (filter.type) {
case types.aliasFilterType.entityList.value: case types.aliasFilterType.entityList.value:
if (filter.stateEntity) { getEntities(filter.entityType, filter.entityList).then(
result.stateEntity = true; function success(entities) {
if (stateParams && stateParams.entityId) { if (entities && entities.length) {
getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then( result.entities = entities;
function success(entity) { deferred.resolve(result);
result.entities = [entity]; } else {
deferred.resolve(result); deferred.reject();
}, }
function fail() { },
deferred.reject(); function fail() {
} deferred.reject();
);
} else {
deferred.resolve(result);
} }
} else { );
getEntities(filter.entityType, filter.entityList).then( break;
function success(entities) { case types.aliasFilterType.entityName.value:
if (entities && entities.length) { getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
result.entities = entities; function success(entities) {
deferred.resolve(result); if (entities && entities.length) {
} else { result.entities = entities;
deferred.reject(); deferred.resolve(result);
} } else {
deferred.reject();
}
},
function fail() {
deferred.reject();
}
);
break;
case types.aliasFilterType.stateEntity.value:
result.stateEntity = true;
if (stateParams && stateParams.entityId) {
getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
function success(entity) {
result.entities = [entity];
deferred.resolve(result);
}, },
function fail() { function fail() {
deferred.reject(); deferred.reject();
} }
); );
} else {
deferred.resolve(result);
} }
break; break;
case types.aliasFilterType.entityName.value: case types.aliasFilterType.assetType.value:
getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then( getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
function success(entities) {
if (entities && entities.length) {
result.entities = entities;
deferred.resolve(result);
} else {
deferred.reject();
}
},
function fail() {
deferred.reject();
}
);
break;
case types.aliasFilterType.deviceType.value:
getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
function success(entities) { function success(entities) {
if (entities && entities.length) { if (entities && entities.length) {
result.entities = entities; result.entities = entities;
@ -337,27 +400,81 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} }
); );
break; break;
//TODO:
//TODO: Alias filter
} }
return deferred.promise; return deferred.promise;
} }
function filterAliasByEntityTypes(entityAlias, entityTypes) { function filterAliasByEntityTypes(entityAlias, entityTypes) {
var filter = entityAlias.filter; var filter = entityAlias.filter;
switch (filter.type) { if (filterAliasFilterTypeByEntityTypes(filter.type, entityTypes)) {
case types.aliasFilterType.entityList.value: switch (filter.type) {
if (filter.stateEntity) { case types.aliasFilterType.entityList.value:
return true;
} else {
return entityTypes.indexOf(filter.entityType) > -1 ? true : false; return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
} case types.aliasFilterType.entityName.value:
return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
case types.aliasFilterType.stateEntity.value:
return true;
case types.aliasFilterType.assetType.value:
return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false;
case types.aliasFilterType.deviceType.value:
return entityTypes.indexOf(types.entityType.device) > -1 ? true : false;
}
}
//TODO: Alias filter
return false;
}
function filterAliasFilterTypeByEntityType(aliasFilterType, entityType) {
switch (aliasFilterType) {
case types.aliasFilterType.entityList.value:
return true;
case types.aliasFilterType.entityName.value: case types.aliasFilterType.entityName.value:
return entityTypes.indexOf(filter.entityType) > -1 ? true : false; return true;
case types.aliasFilterType.stateEntity.value:
return true;
case types.aliasFilterType.assetType.value:
return entityType === types.entityType.asset;
case types.aliasFilterType.deviceType.value:
return entityType === types.entityType.device;
case types.aliasFilterType.relationsQuery.value:
return true;
case types.aliasFilterType.assetSearchQuery.value:
return entityType === types.entityType.asset;
case types.aliasFilterType.deviceSearchQuery.value:
return entityType === types.entityType.device;
} }
//TODO:
return false; return false;
} }
function filterAliasFilterTypeByEntityTypes(aliasFilterType, entityTypes) {
if (!entityTypes || !entityTypes.length) {
return true;
}
var valid = false;
entityTypes.forEach(function(entityType) {
valid = valid || filterAliasFilterTypeByEntityType(aliasFilterType, entityType);
});
return valid;
}
function getAliasFilterTypesByEntityTypes(entityTypes) {
var allAliasFilterTypes = types.aliasFilterType;
if (!entityTypes || !entityTypes.length) {
return allAliasFilterTypes;
}
var result = {};
for (var type in allAliasFilterTypes) {
var aliasFilterType = allAliasFilterTypes[type];
if (filterAliasFilterTypeByEntityTypes(aliasFilterType.value, entityTypes)) {
result[type] = aliasFilterType;
}
}
return result;
}
function checkEntityAlias(entityAlias) { function checkEntityAlias(entityAlias) {
var deferred = $q.defer(); var deferred = $q.defer();
resolveAliasFilter(entityAlias.filter, null, 1).then( resolveAliasFilter(entityAlias.filter, null, 1).then(
@ -380,50 +497,6 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise; return deferred.promise;
} }
/*function processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred) {
if (index < aliasIds.length) {
var aliasId = aliasIds[index];
var entityAlias = entityAliases[aliasId];
var alias = entityAlias.alias;
var filter = entityAlias.filter;
resolveAliasFilter(filter, stateParams).then(
function (entities) {
if (entities && entities.length) {
var entity = entities[0];
var resolvedAlias = {alias: alias, entityType: entity.id.entityType, entityId: entity.id.id};
resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entities);
index++;
processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
} else {
if (!resolution.error) {
resolution.error = 'dashboard.invalid-aliases-config';
}
index++;
processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
}
}
);
} else {
deferred.resolve(resolution);
}
}*/
/*function processEntityAliases(entityAliases, stateParams) {
var deferred = $q.defer();
var resolution = {
aliasesInfo: {}
};
var aliasIds = [];
if (entityAliases) {
for (var aliasId in entityAliases) {
aliasIds.push(aliasId);
}
}
processEntityAlias(0, aliasIds, entityAliases, stateParams, resolution, deferred);
return deferred.promise;
}*/
function getEntityKeys(entityType, entityId, query, type) { function getEntityKeys(entityType, entityId, query, type) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
@ -889,4 +962,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} }
} }
} }

6
ui/src/app/api/subscription.js

@ -258,17 +258,17 @@ export default class Subscription {
} else { } else {
subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false; subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false;
} }
subscription.callbacks.rpcStateChanged(this); subscription.callbacks.rpcStateChanged(subscription);
deferred.resolve(); deferred.resolve();
} else { } else {
subscription.rpcEnabled = false; subscription.rpcEnabled = false;
subscription.callbacks.rpcStateChanged(this); subscription.callbacks.rpcStateChanged(subscription);
deferred.resolve(); deferred.resolve();
} }
}, },
function fail () { function fail () {
subscription.rpcEnabled = false; subscription.rpcEnabled = false;
subscription.callbacks.rpcStateChanged(this); subscription.callbacks.rpcStateChanged(subscription);
deferred.resolve(); deferred.resolve();
} }
); );

4
ui/src/app/common/dashboard-utils.service.js

@ -110,14 +110,12 @@ function DashboardUtils(types, utils, timeService) {
deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value; deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value;
if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList; entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList;
entityAlias.filter.stateEntity = false;
} else { } else {
entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter; entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
} }
} else { } else {
entityAlias.filter.type = types.aliasFilterType.entityList.value; entityAlias.filter.type = types.aliasFilterType.entityList.value;
entityAlias.filter.entityList = [deviceAlias.deviceId]; entityAlias.filter.entityList = [deviceAlias.deviceId];
entityAlias.filter.stateEntity = false;
} }
return entityAlias; return entityAlias;
} }
@ -132,7 +130,6 @@ function DashboardUtils(types, utils, timeService) {
} }
if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
entityAlias.filter.entityList = entityAlias.entityFilter.entityList; entityAlias.filter.entityList = entityAlias.entityFilter.entityList;
entityAlias.filter.stateEntity = false;
} else { } else {
entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter; entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter;
} }
@ -342,7 +339,6 @@ function DashboardUtils(types, utils, timeService) {
function createSingleEntityFilter(entityType, entityId) { function createSingleEntityFilter(entityType, entityId) {
return { return {
type: types.aliasFilterType.entityList.value, type: types.aliasFilterType.entityList.value,
stateEntity: false,
entityList: [entityId], entityList: [entityId],
entityType: entityType, entityType: entityType,
resolveMultiple: false resolveMultiple: false

51
ui/src/app/common/types.constant.js

@ -74,6 +74,10 @@ export default angular.module('thingsboard.types', [])
value: 'entityName', value: 'entityName',
name: 'alias.filter-type-entity-name' name: 'alias.filter-type-entity-name'
}, },
stateEntity: {
value: 'stateEntity',
name: 'alias.filter-type-state-entity'
},
assetType: { assetType: {
value: 'assetType', value: 'assetType',
name: 'alias.filter-type-asset-type' name: 'alias.filter-type-asset-type'
@ -139,6 +143,53 @@ export default angular.module('thingsboard.types', [])
dashboard: "DASHBOARD", dashboard: "DASHBOARD",
alarm: "ALARM" alarm: "ALARM"
}, },
entityTypeTranslations: {
"DEVICE": {
type: 'entity.type-device',
list: 'entity.list-of-devices',
nameStartsWith: 'entity.device-name-starts-with'
},
"ASSET": {
type: 'entity.type-asset',
list: 'entity.list-of-assets',
nameStartsWith: 'entity.asset-name-starts-with'
},
"RULE": {
type: 'entity.type-rule',
list: 'entity.list-of-rules',
nameStartsWith: 'entity.rule-name-starts-with'
},
"PLUGIN": {
type: 'entity.type-plugin',
list: 'entity.list-of-plugins',
nameStartsWith: 'entity.plugin-name-starts-with'
},
"TENANT": {
type: 'entity.type-tenant',
list: 'entity.list-of-tenants',
nameStartsWith: 'entity.tenant-name-starts-with'
},
"CUSTOMER": {
type: 'entity.type-customer',
list: 'entity.list-of-customers',
nameStartsWith: 'entity.customer-name-starts-with'
},
"USER": {
type: 'entity.type-user',
list: 'entity.list-of-users',
nameStartsWith: 'entity.user-name-starts-with'
},
"DASHBOARD": {
type: 'entity.type-dashboard',
list: 'entity.list-of-dashboards',
nameStartsWith: 'entity.dashboard-name-starts-with'
},
"ALARM": {
type: 'entity.type-alarm',
list: 'entity.list-of-alarms',
nameStartsWith: 'entity.alarm-name-starts-with'
}
},
entitySearchDirection: { entitySearchDirection: {
from: "FROM", from: "FROM",
to: "TO" to: "TO"

26
ui/src/app/common/utils.service.js

@ -109,8 +109,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
cleanCopy: cleanCopy, cleanCopy: cleanCopy,
isLocalUrl: isLocalUrl, isLocalUrl: isLocalUrl,
validateDatasources: validateDatasources, validateDatasources: validateDatasources,
createKey: createKey, createKey: createKey
entityTypeName: entityTypeName
} }
return service; return service;
@ -358,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
return dataKey; return dataKey;
} }
function entityTypeName (type) {
switch (type) {
case types.entityType.device:
return 'entity.type-device';
case types.entityType.asset:
return 'entity.type-asset';
case types.entityType.rule:
return 'entity.type-rule';
case types.entityType.plugin:
return 'entity.type-plugin';
case types.entityType.tenant:
return 'entity.type-tenant';
case types.entityType.customer:
return 'entity.type-customer';
case types.entityType.user:
return 'entity.type-user';
case types.entityType.dashboard:
return 'entity.type-dashboard';
case types.entityType.alarm:
return 'entity.type-alarm';
}
}
} }

17
ui/src/app/dashboard/add-widget.controller.js

@ -15,7 +15,7 @@
*/ */
/* eslint-disable import/no-unresolved, import/default */ /* eslint-disable import/no-unresolved, import/default */
import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
@ -130,17 +130,14 @@ export default function AddWidgetController($scope, widgetService, entityService
var singleEntityAlias = {id: null, alias: alias, filter: {}}; var singleEntityAlias = {id: null, alias: alias, filter: {}};
$mdDialog.show({ $mdDialog.show({
controller: 'EntityAliasesController', controller: 'EntityAliasDialogController',
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: entityAliasesTemplate, templateUrl: entityAliasDialogTemplate,
locals: { locals: {
config: { isAdd: true,
entityAliases: angular.copy(vm.dashboard.configuration.entityAliases), allowedEntityTypes: allowedEntityTypes,
widgets: null, entityAliases: vm.dashboard.configuration.entityAliases,
isSingleEntityAlias: true, alias: singleEntityAlias
singleEntityAlias: singleEntityAlias,
allowedEntityTypes: allowedEntityTypes
}
}, },
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
fullscreen: true, fullscreen: true,

17
ui/src/app/dashboard/edit-widget.directive.js

@ -15,7 +15,7 @@
*/ */
/* eslint-disable import/no-unresolved, import/default */ /* eslint-disable import/no-unresolved, import/default */
import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
import editWidgetTemplate from './edit-widget.tpl.html'; import editWidgetTemplate from './edit-widget.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
@ -98,17 +98,14 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
var singleEntityAlias = {id: null, alias: alias, filter: {}}; var singleEntityAlias = {id: null, alias: alias, filter: {}};
$mdDialog.show({ $mdDialog.show({
controller: 'EntityAliasesController', controller: 'EntityAliasDialogController',
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: entityAliasesTemplate, templateUrl: entityAliasDialogTemplate,
locals: { locals: {
config: { isAdd: true,
entityAliases: angular.copy(scope.dashboard.configuration.entityAliases), allowedEntityTypes: allowedEntityTypes,
widgets: null, entityAliases: scope.dashboard.configuration.entityAliases,
isSingleEntityAlias: true, alias: singleEntityAlias
singleEntityAlias: singleEntityAlias,
allowedEntityTypes: allowedEntityTypes
}
}, },
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
fullscreen: true, fullscreen: true,

69
ui/src/app/entity/entity-filter-dialog.controller.js → ui/src/app/entity/entity-alias-dialog.controller.js

@ -14,22 +14,48 @@
* limitations under the License. * limitations under the License.
*/ */
import './entity-alias-dialog.scss';
/*@ngInject*/ /*@ngInject*/
export default function EntityFilterDialogController($scope, $mdDialog, $q, entityService, types, isAdd, allowedEntityTypes, filter) { export default function EntityAliasDialogController($scope, $mdDialog, $q, $filter, utils, entityService, types, isAdd, allowedEntityTypes, entityAliases, alias) {
var vm = this; var vm = this;
vm.types = types; vm.types = types;
vm.isAdd = isAdd; vm.isAdd = isAdd;
vm.allowedEntityTypes = allowedEntityTypes; vm.allowedEntityTypes = allowedEntityTypes;
vm.filter = filter; if (angular.isArray(entityAliases)) {
vm.entityAliases = entityAliases;
} else {
vm.entityAliases = [];
for (var aliasId in entityAliases) {
vm.entityAliases.push(entityAliases[aliasId]);
}
}
if (vm.isAdd && !alias) {
vm.alias = {
alias: '',
filter: {
resolveMultiple: false
}
};
} else {
vm.alias = alias;
}
vm.cancel = cancel; vm.cancel = cancel;
vm.save = save; vm.save = save;
$scope.$watch('vm.filter.type', function (newType, prevType) { $scope.$watch('vm.alias.alias', function (newAlias) {
if (newType && newType != prevType) { if (newAlias) {
updateFilter(); var valid = true;
var result = $filter('filter')(vm.entityAliases, {alias: newAlias}, true);
if (result && result.length) {
if (vm.isAdd || vm.alias.id != result[0].id) {
valid = false;
}
}
$scope.theForm.aliasName.$setValidity('duplicateAliasName', valid);
} }
}); });
@ -39,32 +65,13 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
} }
}); });
function updateFilter() {
var filter = {};
filter.type = vm.filter.type;
filter.resolveMultiple = vm.filter.resolveMultiple;
switch (filter.type) {
case types.aliasFilterType.entityList.value:
filter.entityType = null;
filter.entityList = [];
filter.stateEntity = false;
break;
case types.aliasFilterType.entityName.value:
filter.entityType = null;
filter.entityNameFilter = '';
break;
//TODO:
}
vm.filter = filter;
}
function validate() { function validate() {
var deferred = $q.defer(); var deferred = $q.defer();
var validationResult = { var validationResult = {
entity: null, entity: null,
stateEntity: false stateEntity: false
} }
entityService.resolveAliasFilter(vm.filter, null, 1).then( entityService.resolveAliasFilter(vm.alias.filter, null, 1).then(
function success(result) { function success(result) {
validationResult.stateEntity = result.stateEntity; validationResult.stateEntity = result.stateEntity;
var entities = result.entities; var entities = result.entities;
@ -87,12 +94,11 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
function save() { function save() {
$scope.theForm.$setPristine(); $scope.theForm.$setPristine();
validate().then( validate().then(
function success(validationResult) { function success() {
$mdDialog.hide({ if (vm.isAdd) {
filter: vm.filter, vm.alias.id = utils.guid();
entity: validationResult.entity, }
stateEntity: validationResult.stateEntity $mdDialog.hide(vm.alias);
});
}, },
function fail() { function fail() {
$scope.theForm.$setValidity('entityFilter', false); $scope.theForm.$setValidity('entityFilter', false);
@ -101,4 +107,3 @@ export default function EntityFilterDialogController($scope, $mdDialog, $q, enti
} }
} }

27
ui/src/app/entity/entity-alias-dialog.scss

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-entity-alias-dialog {
.tb-resolve-multiple-switch {
padding-left: 10px;
.resolve-multiple-switch {
margin: 0;
}
.resolve-multiple-label {
margin: 5px 0;
}
}
}

73
ui/src/app/entity/entity-alias-dialog.tpl.html

@ -0,0 +1,73 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog class="tb-entity-alias-dialog" style="width: 600px;" aria-label="{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}">
<form name="theForm" ng-submit="vm.save()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset ng-disabled="loading">
<div flex layout="column">
<div layout="row">
<md-input-container flex class="md-block">
<label translate>alias.name</label>
<input required name="aliasName"
ng-model="vm.alias.alias"
aria-label="{{ 'alias.name' | translate }}">
<div ng-messages="theForm.aliasName.$error">
<div ng-message="required" translate>alias.name-required</div>
<div ng-message="duplicateAliasName" translate>alias.duplicate-alias</div>
</div>
</md-input-container>
<section class="tb-resolve-multiple-switch" layout="column" layout-align="start center">
<label class="tb-small resolve-multiple-label" translate>alias.resolve-multiple</label>
<md-switch class="resolve-multiple-switch" ng-model="vm.alias.filter.resolveMultiple"
aria-label="{{ 'alias.resolve-multiple' | translate }}">
</md-switch>
</section>
</div>
<tb-entity-filter
ng-model="vm.alias.filter"
allowed-entity-types="vm.allowedEntityTypes"
the-form="theForm">
</tb-entity-filter>
<div class="tb-error-messages" ng-messages="theForm.$error" role="alert">
<div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div>
</div>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions>
</form>
</md-dialog>

133
ui/src/app/entity/entity-aliases.controller.js

@ -15,22 +15,27 @@
*/ */
import './entity-aliases.scss'; import './entity-aliases.scss';
/* eslint-disable import/no-unresolved, import/default */
import entityAliasDialogTemplate from './entity-alias-dialog.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/ /*@ngInject*/
export default function EntityAliasesController(utils, entityService, toast, $scope, $mdDialog, $document, $q, $translate, export default function EntityAliasesController(utils, entityService, toast, $scope, $mdDialog, $document, $q, $translate,
types, config) { types, config) {
var vm = this; var vm = this;
vm.isSingleEntityAlias = config.isSingleEntityAlias; vm.types = types;
vm.singleEntityAlias = config.singleEntityAlias;
vm.entityAliases = []; vm.entityAliases = [];
vm.title = config.customTitle ? config.customTitle : 'entity.aliases'; vm.title = config.customTitle ? config.customTitle : 'entity.aliases';
vm.disableAdd = config.disableAdd; vm.disableAdd = config.disableAdd;
vm.aliasToWidgetsMap = {}; vm.aliasToWidgetsMap = {};
vm.allowedEntityTypes = config.allowedEntityTypes; vm.allowedEntityTypes = config.allowedEntityTypes;
vm.onFilterEntityChanged = onFilterEntityChanged;
vm.addAlias = addAlias; vm.addAlias = addAlias;
vm.editAlias = editAlias;
vm.removeAlias = removeAlias; vm.removeAlias = removeAlias;
vm.cancel = cancel; vm.cancel = cancel;
@ -79,42 +84,61 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
} }
} }
if (vm.isSingleEntityAlias) {
checkEntityAlias(vm.singleEntityAlias);
}
for (aliasId in config.entityAliases) { for (aliasId in config.entityAliases) {
var entityAlias = config.entityAliases[aliasId]; var entityAlias = config.entityAliases[aliasId];
var result = {id: aliasId, alias: entityAlias.alias, filter: entityAlias.filter, changed: true}; var filter = entityAlias.filter;
checkEntityAlias(result); if (!filter) {
filter = {
resolveMultiple: false
};
}
if (!filter.resolveMultiple) {
filter.resolveMultiple = false;
}
var result = {id: aliasId, alias: entityAlias.alias, filter: filter};
vm.entityAliases.push(result); vm.entityAliases.push(result);
} }
} }
function checkEntityAlias(entityAlias) { function addAlias($event) {
if (!entityAlias.filter || entityAlias.filter == null) { openAliasDialog($event);
entityAlias.filter = {};
}
} }
function onFilterEntityChanged(entity, stateEntity, entityAlias) { function editAlias($event, entityAlias) {
if (entityAlias) { openAliasDialog($event, entityAlias);
if (!entityAlias.alias || entityAlias.alias.length == 0) {
entityAlias.changed = false;
}
if (!entityAlias.changed && entityAlias.filter && entityAlias.filter.type) {
if (stateEntity) {
entityAlias.alias = $translate.instant('alias.state-entity');
} else {
entityAlias.alias = entity.name;
}
}
}
} }
function addAlias() { function openAliasDialog($event, entityAlias) {
var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false}; var isAdd = entityAlias ? false : true;
vm.entityAliases.push(entityAlias); var aliasIndex;
if (!isAdd) {
aliasIndex = vm.entityAliases.indexOf(entityAlias);
}
$mdDialog.show({
controller: 'EntityAliasDialogController',
controllerAs: 'vm',
templateUrl: entityAliasDialogTemplate,
locals: {
isAdd: isAdd,
allowedEntityTypes: vm.allowedEntityTypes,
entityAliases: vm.entityAliases,
alias: isAdd ? null : angular.copy(entityAlias)
},
parent: angular.element($document[0].body),
fullscreen: true,
skipHide: true,
targetEvent: $event
}).then(function (alias) {
if (isAdd) {
vm.entityAliases.push(alias);
} else {
vm.entityAliases[aliasIndex] = alias;
}
if ($scope.theForm) {
$scope.theForm.$setDirty();
}
}, function () {
});
} }
function removeAlias($event, entityAlias) { function removeAlias($event, entityAlias) {
@ -157,43 +181,30 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
var uniqueAliasList = {}; var uniqueAliasList = {};
var valid = true; var valid = true;
var aliasId; var message, aliasId, alias, filter;
var alias;
var i; for (var i = 0; i < vm.entityAliases.length; i++) {
aliasId = vm.entityAliases[i].id;
if (vm.isSingleEntityAlias) { alias = vm.entityAliases[i].alias;
if (!vm.singleEntityAlias.id) { filter = vm.entityAliases[i].filter;
vm.singleEntityAlias.id = utils.guid(); if (uniqueAliasList[alias]) {
} valid = false;
for (i = 0; i < vm.entityAliases.length; i ++) { message = $translate.instant('entity.duplicate-alias-error', {alias: alias});
alias = vm.entityAliases[i].alias; break;
if (alias === vm.singleEntityAlias.alias) { } else if (!filter || !filter.type) {
valid = false; valid = false;
break; message = $translate.instant('entity.missing-entity-filter-error', {alias: alias});
} break;
} } else {
} else { uniqueAliasList[alias] = alias;
for (i = 0; i < vm.entityAliases.length; i++) { entityAliases[aliasId] = {id: aliasId, alias: alias, filter: filter};
aliasId = vm.entityAliases[i].id;
alias = vm.entityAliases[i].alias;
if (!uniqueAliasList[alias]) {
uniqueAliasList[alias] = alias;
entityAliases[aliasId] = {id: aliasId, alias: alias, filter: vm.entityAliases[i].filter};
} else {
valid = false;
break;
}
} }
} }
if (valid) { if (valid) {
$scope.theForm.$setPristine(); $scope.theForm.$setPristine();
if (vm.isSingleEntityAlias) { $mdDialog.hide(entityAliases);
$mdDialog.hide(vm.singleEntityAlias);
} else {
$mdDialog.hide(entityAliases);
}
} else { } else {
toast.showError($translate.instant('entity.duplicate-alias-error', {alias: alias})); toast.showError(message);
} }
} }

12
ui/src/app/entity/entity-aliases.scss

@ -17,6 +17,7 @@
.tb-aliases-dialog { .tb-aliases-dialog {
.md-dialog-content { .md-dialog-content {
padding-bottom: 0px; padding-bottom: 0px;
padding-top: 0px;
} }
.tb-aliases-header { .tb-aliases-header {
min-height: 40px; min-height: 40px;
@ -33,5 +34,16 @@
md-input-container { md-input-container {
margin: 0px; margin: 0px;
} }
.tb-resolve-multiple-switch {
padding-left: 10px;
.resolve-multiple-switch {
margin: 0;
}
}
.md-button {
&.md-icon-button {
margin: 0px;
}
}
} }
} }

98
ui/src/app/entity/entity-aliases.tpl.html

@ -19,7 +19,7 @@
<form name="theForm" ng-submit="vm.save()"> <form name="theForm" ng-submit="vm.save()">
<md-toolbar> <md-toolbar>
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<h2>{{ vm.isSingleEntityAlias ? ('entity.configure-alias' | translate:vm.singleEntityAlias ) : (vm.title | translate) }}</h2> <h2>{{ vm.title | translate }}</h2>
<span flex></span> <span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()"> <md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
@ -28,66 +28,72 @@
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
<div class="tb-aliases-header" ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> <div class="tb-aliases-header" flex layout="row" layout-align="start center">
<span flex="5"></span> <span flex="5"></span>
<div flex layout="row" layout-align="start center"> <div flex layout="row" layout-align="start center">
<span class="tb-header-label" translate flex="20" style="min-width: 150px;">entity.alias</span> <span class="tb-header-label" translate flex="20" style="min-width: 150px;">alias.name</span>
<div flex="80" layout="row" layout-align="start center" style="min-width: 240px; padding-left: 10px;"> <span class="tb-header-label" translate flex="70" style="padding-left: 10px;">alias.entity-filter</span>
<span class="tb-header-label" translate flex="70">alias.entity-filter</span> <span class="tb-header-label" translate flex="10" style="padding-left: 10px; min-width: 120px;">alias.resolve-multiple</span>
<span class="tb-header-label" translate flex="30" style="padding-left: 10px;" >alias.resolve-multiple</span> <span style="min-width: 80px;"></span>
</div>
<span style="min-width: 40px; margin: 0 6px;"></span>
</div> </div>
</div> </div>
<md-divider ng-show="!vm.isSingleEntityAlias"></md-divider> <md-divider></md-divider>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="loading">
<div ng-show="vm.isSingleEntityAlias" layout="row"> <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
<tb-entity-filter flex <span flex="5">{{$index + 1}}.</span>
allowed-entity-types="vm.allowedEntityTypes" <di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
ng-model="vm.singleEntityAlias.filter"> <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
</tb-entity-filter> <input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
</div> <div ng-messages="aliasForm.alias.$error">
<div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;"> <div translate ng-message="required">entity.alias-required</div>
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> </div>
<span flex="5">{{$index + 1}}.</span> </md-input-container>
<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> <tb-entity-filter-view
<md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block"> flex="70" style="padding-left: 10px;"
<input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> ng-model="entityAlias.filter">
<div ng-messages="aliasForm.alias.$error"> </tb-entity-filter-view>
<div translate ng-message="required">entity.alias-required</div> <section flex="10" style="padding-left: 10px; min-width: 120px;"
</div> class="tb-resolve-multiple-switch"
</md-input-container> layout="column"
<tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;" layout-align="center center">
hide-labels <md-switch class="resolve-multiple-switch"
allowed-entity-types="vm.allowedEntityTypes" ng-model="entityAlias.filter.resolveMultiple"
ng-model="entityAlias.filter" aria-label="resolve-multiple-switcher">
on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)"> </md-switch>
</tb-entity-filter> </section>
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}"> ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'entity.remove-alias' | translate }} {{ 'alias.edit' | translate }}
</md-tooltip> </md-tooltip>
<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons"> <md-icon aria-label="{{ 'alias.edit' | translate }}" class="material-icons">
close edit
</md-icon> </md-icon>
</md-button> </md-button>
</div> <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
</div> ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
<md-tooltip md-direction="top">
{{ 'entity.remove-alias' | translate }}
</md-tooltip>
<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">
close
</md-icon>
</md-button>
</di>
</div> </div>
</fieldset> </fieldset>
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<md-button ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" <md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
ng-click="vm.addAlias($event)" ng-click="vm.addAlias($event)"
aria-label="{{ 'action.add' | translate }}"> aria-label="{{ 'alias.add' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'entity.add-alias' | translate }} {{ 'alias.add' | translate }}
</md-tooltip> </md-tooltip>
<span translate>action.add</span> <span translate>alias.add</span>
</md-button> </md-button>
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">

100
ui/src/app/entity/entity-filter-dialog.tpl.html

@ -1,100 +0,0 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog class="tb-entity-filter-dialog" style="width: 600px;" aria-label="{{ 'alias.entity-filter' | translate }}">
<form name="theForm" ng-submit="vm.save()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>{{ (vm.isAdd ? 'alias.create-entity-filter' : 'alias.edit-entity-filter') | translate }}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset ng-disabled="loading">
<div flex layout="column">
<md-input-container>
<label>{{ 'alias.filter-type' | translate }}</label>
<md-select required name="filterType"
ng-model="vm.filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
<md-option ng-repeat="type in vm.types.aliasFilterType" ng-value="type.value">
{{type.name | translate}}
</md-option>
</md-select>
<div ng-messages="theForm.filterType.$error">
<div ng-message="required" translate>alias.filter-type-required</div>
</div>
</md-input-container>
<section layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityList.value" id="entityListFilter">
<md-checkbox flex aria-label="{{ 'alias.use-state-entity' | translate }}"
ng-model="vm.filter.stateEntity">{{ 'alias.use-state-entity' | translate }}
</md-checkbox>
<tb-entity-type-select
ng-if="!vm.filter.stateEntity"
ng-model="vm.filter.entityType"
the-form="theForm"
ng-disabled="vm.filter.stateEntity"
tb-required="!vm.filter.stateEntity"
allowed-entity-types="vm.allowedEntityTypes">
</tb-entity-type-select>
<tb-entity-list
ng-if="!vm.filter.stateEntity"
ng-model="vm.filter.entityList"
ng-disabled="vm.filter.stateEntity"
tb-required="!vm.filter.stateEntity"
entity-type="vm.filter.entityType">
</tb-entity-list>
</section>
<section flex layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityName.value" id="entityNameFilter">
<tb-entity-type-select
ng-model="vm.filter.entityType"
the-form="theForm"
tb-required="true"
allowed-entity-types="vm.allowedEntityTypes">
</tb-entity-type-select>
<md-input-container flex>
<label translate>entity.name-starts-with</label>
<input required name="entityNameFilter"
ng-model="vm.filter.entityNameFilter"
aria-label="{{ 'entity.name-starts-with' | translate }}">
<div ng-messages="theForm.entityNameFilter.$error">
<div ng-message="required" translate>entity.entity-name-filter-required</div>
</div>
</md-input-container>
</section>
<div class="tb-error-messages" ng-messages="theForm.$error" role="alert">
<div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div>
</div>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }}
</md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions>
</form>
</md-dialog>

106
ui/src/app/entity/entity-filter-view.directive.js

@ -0,0 +1,106 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import entityFilterViewTemplate from './entity-filter-view.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
import './entity-filter-view.scss';
/*@ngInject*/
export default function EntityFilterViewDirective($compile, $templateCache, $q, $document, $mdDialog, $translate, types/*, entityService*/) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(entityFilterViewTemplate);
element.html(template);
scope.ngModelCtrl = ngModelCtrl;
scope.types = types;
scope.filterDisplayValue = '';
scope.$watch('filter', function () {
scope.updateDisplayValue();
});
scope.updateDisplayValue = function() {
if (scope.filter && scope.filter.type) {
var entityType;
var prefix;
switch (scope.filter.type) {
case types.aliasFilterType.entityList.value:
entityType = scope.filter.entityType;
var count = scope.filter.entityList.length;
scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].list, {count: count}, 'messageformat');
break;
case types.aliasFilterType.entityName.value:
entityType = scope.filter.entityType;
prefix = scope.filter.entityNameFilter;
scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].nameStartsWith, {prefix: prefix});
break;
case types.aliasFilterType.stateEntity.value:
scope.filterDisplayValue = $translate.instant('alias.filter-type-state-entity-description');
break;
case types.aliasFilterType.assetType.value:
var assetType = scope.filter.assetType;
prefix = scope.filter.assetNameFilter;
if (prefix && prefix.length) {
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-and-name-description', {assetType: assetType, prefix: prefix});
} else {
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-description', {assetType: assetType});
}
break;
case types.aliasFilterType.deviceType.value:
var deviceType = scope.filter.deviceType;
prefix = scope.filter.deviceNameFilter;
if (prefix && prefix.length) {
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-and-name-description', {deviceType: deviceType, prefix: prefix});
} else {
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType});
}
break;
//TODO: Alias filter
default:
scope.filterDisplayValue = scope.filter.type;
break;
}
} else {
scope.filterDisplayValue = '';
}
}
ngModelCtrl.$render = function () {
if (ngModelCtrl.$viewValue) {
scope.filter = ngModelCtrl.$viewValue;
} else {
scope.filter = null;
}
}
$compile(element.contents())(scope);
}
return {
restrict: "E",
require: "^ngModel",
link: linker,
scope: true
};
}

33
ui/src/app/entity/entity-filter-view.scss

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-entity-filter-view {
.entity-filter-empty {
color: rgba(221, 44, 0, 0.87);
font-size: 14px;
line-height: 16px;
}
.entity-filter-type {
font-size: 14px;
line-height: 16px;
color: rgba(0, 0, 0, 0.570588);
}
.entity-filter-value {
font-size: 14px;
line-height: 16px;
color: rgba(0, 0, 0, 0.570588);
}
}

24
ui/src/app/entity/entity-filter-view.tpl.html

@ -0,0 +1,24 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div layout='column' class="tb-entity-filter-view">
<div ng-if="!filter || !filter.type" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div>
<div ng-if="filter && filter.type" layout="column">
<div class="entity-filter-value">{{ filterDisplayValue }}</div>
</div>
</div>

100
ui/src/app/entity/entity-filter.directive.js

@ -17,16 +17,13 @@
/* eslint-disable import/no-unresolved, import/default */ /* eslint-disable import/no-unresolved, import/default */
import entityFilterTemplate from './entity-filter.tpl.html'; import entityFilterTemplate from './entity-filter.tpl.html';
import entityFilterDialogTemplate from './entity-filter-dialog.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
import EntityFilterDialogController from './entity-filter-dialog.controller';
import './entity-filter.scss'; import './entity-filter.scss';
/*@ngInject*/ /*@ngInject*/
export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types) { export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService) {
var linker = function (scope, element, attrs, ngModelCtrl) { var linker = function (scope, element, attrs, ngModelCtrl) {
@ -35,66 +32,59 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
scope.ngModelCtrl = ngModelCtrl; scope.ngModelCtrl = ngModelCtrl;
scope.types = types; scope.types = types;
scope.hideLabels = angular.isDefined(attrs.hideLabels); scope.aliasFilterTypes = entityService.getAliasFilterTypesByEntityTypes(scope.allowedEntityTypes);
scope.updateValidity = function() { scope.$watch('filter.type', function (newType, prevType) {
if (ngModelCtrl.$viewValue) { if (newType && newType != prevType) {
var value = ngModelCtrl.$viewValue; updateFilter();
ngModelCtrl.$setValidity('filter', value.type ? true : false);
} }
} });
ngModelCtrl.$render = function () { function updateFilter() {
if (ngModelCtrl.$viewValue) { var filter = {};
scope.model = angular.copy(ngModelCtrl.$viewValue); filter.type = scope.filter.type;
} else { filter.resolveMultiple = scope.filter.resolveMultiple;
scope.model = { switch (filter.type) {
type: null, case types.aliasFilterType.entityList.value:
resolveMultiple: false filter.entityType = null;
} filter.entityList = [];
break;
case types.aliasFilterType.entityName.value:
filter.entityType = null;
filter.entityNameFilter = '';
break;
case types.aliasFilterType.stateEntity.value:
break;
case types.aliasFilterType.assetType.value:
filter.assetType = null;
filter.assetNameFilter = '';
break;
case types.aliasFilterType.deviceType.value:
filter.deviceType = null;
filter.deviceNameFilter = '';
break;
//TODO: Alias filter
} }
scope.filter = filter;
} }
scope.$watch('model.resolveMultiple', function () { scope.$watch('filter', function () {
if (ngModelCtrl.$viewValue) { scope.updateView();
var value = ngModelCtrl.$viewValue;
value.resolveMultiple = scope.model.resolveMultiple;
ngModelCtrl.$setViewValue(value);
scope.updateValidity();
}
}); });
scope.editFilter = function($event) { scope.updateView = function() {
openEntityFilterDialog($event, false); ngModelCtrl.$setViewValue(scope.filter);
} }
scope.createFilter = function($event) { ngModelCtrl.$render = function () {
openEntityFilterDialog($event, true); if (ngModelCtrl.$viewValue) {
} scope.filter = ngModelCtrl.$viewValue;
} else {
function openEntityFilterDialog($event, isAdd) { scope.filter = {
$mdDialog.show({ type: null,
controller: EntityFilterDialogController, resolveMultiple: false
controllerAs: 'vm',
templateUrl: entityFilterDialogTemplate,
locals: {
isAdd: isAdd,
allowedEntityTypes: scope.allowedEntityTypes,
filter: angular.copy(scope.model)
},
parent: angular.element($document[0].body),
fullscreen: true,
skipHide: true,
targetEvent: $event
}).then(function (result) {
scope.model = result.filter;
ngModelCtrl.$setViewValue(result.filter);
scope.updateValidity();
if (scope.onMatchingEntityChange) {
scope.onMatchingEntityChange({entity: result.entity, stateEntity: result.stateEntity});
} }
}, function () { }
});
} }
$compile(element.contents())(scope); $compile(element.contents())(scope);
@ -106,8 +96,8 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
require: "^ngModel", require: "^ngModel",
link: linker, link: linker,
scope: { scope: {
allowedEntityTypes: '=?', theForm: '=',
onMatchingEntityChange: '&' allowedEntityTypes: '=?'
} }
}; };

19
ui/src/app/entity/entity-filter.scss

@ -13,22 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
.tb-entity-filter { .tb-entity-filter {
.tb-filter-switch {
padding-left: 10px;
.filter-switch {
margin: 0;
}
.filter-label {
margin: 5px 0;
}
}
.tb-error-messages {
margin-top: -11px;
height: 35px;
.tb-error-message {
padding-left: 8px;
padding-top: 14px;
}
}
} }

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

@ -15,37 +15,77 @@
limitations under the License. limitations under the License.
--> -->
<section layout='row' class="tb-entity-filter"> <div layout='column' class="tb-entity-filter">
<section layout="row" flex="70"> <md-input-container class="md-block">
<section flex layout="column" layout-align="center start"> <label>{{ 'alias.filter-type' | translate }}</label>
<div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div> <md-select required name="filterType"
<md-button ng-if="!model.type" ng-model="filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
ng-disabled="loading" class="md-primary" <md-option ng-repeat="type in aliasFilterTypes" ng-value="type.value">
ng-click="createFilter($event)" {{type.name | translate}}
aria-label="{{ 'alias.create-entity-filter' | translate }}"> </md-option>
<md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}" </md-select>
class="material-icons"> <div ng-messages="theForm.filterType.$error">
add <div ng-message="required" translate>alias.filter-type-required</div>
</md-icon> </div>
{{ 'alias.create-entity-filter' | translate }} </md-input-container>
</md-button> <section layout="column" ng-if="filter.type == types.aliasFilterType.entityList.value" id="entityListFilter">
</section> <tb-entity-type-select
<md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary" ng-model="filter.entityType"
style="min-width: 40px;" the-form="theForm"
ng-click="editFilter($event)" tb-required="true"
aria-label="{{ 'alias.edit-entity-filter' | translate }}"> allowed-entity-types="allowedEntityTypes">
<md-tooltip md-direction="top"> </tb-entity-type-select>
{{ 'alias.edit-entity-filter' | translate }} <tb-entity-list
</md-tooltip> ng-model="filter.entityList"
<md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}" tb-required="true"
class="material-icons"> entity-type="filter.entityType">
edit </tb-entity-list>
</md-icon>
</md-button>
</section> </section>
<section class="tb-filter-switch" layout="column" flex="30" layout-align="center center"> <section flex layout="column" ng-if="filter.type == types.aliasFilterType.entityName.value" id="entityNameFilter">
<label ng-if="!hideLabels" class="tb-small filter-label" translate>alias.resolve-multiple</label> <tb-entity-type-select
<md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher"> ng-model="filter.entityType"
</md-switch> the-form="theForm"
tb-required="true"
allowed-entity-types="allowedEntityTypes">
</tb-entity-type-select>
<md-input-container class="md-block">
<label translate>entity.name-starts-with</label>
<input required name="entityNameFilter"
ng-model="filter.entityNameFilter"
aria-label="{{ 'entity.name-starts-with' | translate }}">
<div ng-messages="theForm.entityNameFilter.$error">
<div ng-message="required" translate>entity.entity-name-filter-required</div>
</div>
</md-input-container>
</section> </section>
</section> <section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
</section>
<section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
<tb-entity-subtype-autocomplete
tb-required="true"
the-form="theForm"
ng-model="filter.assetType"
entity-type="types.entityType.asset">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>asset.name-starts-with</label>
<input name="assetNameFilter"
ng-model="filter.assetNameFilter"
aria-label="{{ 'asset.name-starts-with' | translate }}">
</md-input-container>
</section>
<section layout="column" ng-if="filter.type == types.aliasFilterType.deviceType.value" id="deviceTypeFilter">
<tb-entity-subtype-autocomplete
tb-required="true"
the-form="theForm"
ng-model="filter.deviceType"
entity-type="types.entityType.device">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>device.name-starts-with</label>
<input name="deviceNameFilter"
ng-model="filter.deviceNameFilter"
aria-label="{{ 'device.name-starts-with' | translate }}">
</md-input-container>
</section>
</div>

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

@ -71,7 +71,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
} }
scope.typeName = function(type) { scope.typeName = function(type) {
return utils.entityTypeName(type); return type ? types.entityTypeTranslations[type].type : '';
} }
scope.updateValidity = function () { scope.updateValidity = function () {

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

@ -15,6 +15,7 @@
*/ */
import EntityAliasesController from './entity-aliases.controller'; import EntityAliasesController from './entity-aliases.controller';
import EntityAliasDialogController from './entity-alias-dialog.controller';
import EntityTypeSelectDirective from './entity-type-select.directive'; import EntityTypeSelectDirective from './entity-type-select.directive';
import EntitySubtypeSelectDirective from './entity-subtype-select.directive'; import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive'; import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
@ -22,6 +23,7 @@ import EntityAutocompleteDirective from './entity-autocomplete.directive';
import EntityListDirective from './entity-list.directive'; import EntityListDirective from './entity-list.directive';
import EntitySelectDirective from './entity-select.directive'; import EntitySelectDirective from './entity-select.directive';
import EntityFilterDirective from './entity-filter.directive'; import EntityFilterDirective from './entity-filter.directive';
import EntityFilterViewDirective from './entity-filter-view.directive';
import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
import AliasesEntitySelectDirective from './aliases-entity-select.directive'; import AliasesEntitySelectDirective from './aliases-entity-select.directive';
import AddAttributeDialogController from './attribute/add-attribute-dialog.controller'; import AddAttributeDialogController from './attribute/add-attribute-dialog.controller';
@ -32,6 +34,7 @@ import RelationTypeAutocompleteDirective from './relation/relation-type-autocomp
export default angular.module('thingsboard.entity', []) export default angular.module('thingsboard.entity', [])
.controller('EntityAliasesController', EntityAliasesController) .controller('EntityAliasesController', EntityAliasesController)
.controller('EntityAliasDialogController', EntityAliasDialogController)
.controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController) .controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController)
.controller('AddAttributeDialogController', AddAttributeDialogController) .controller('AddAttributeDialogController', AddAttributeDialogController)
.controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController) .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController)
@ -42,6 +45,7 @@ export default angular.module('thingsboard.entity', [])
.directive('tbEntityList', EntityListDirective) .directive('tbEntityList', EntityListDirective)
.directive('tbEntitySelect', EntitySelectDirective) .directive('tbEntitySelect', EntitySelectDirective)
.directive('tbEntityFilter', EntityFilterDirective) .directive('tbEntityFilter', EntityFilterDirective)
.directive('tbEntityFilterView', EntityFilterViewDirective)
.directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
.directive('tbAttributeTable', AttributeTableDirective) .directive('tbAttributeTable', AttributeTableDirective)
.directive('tbRelationTable', RelationTableDirective) .directive('tbRelationTable', RelationTableDirective)

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

@ -218,9 +218,9 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
function success(allRelations) { function success(allRelations) {
allRelations.forEach(function(relation) { allRelations.forEach(function(relation) {
if (vm.direction == vm.types.entitySearchDirection.from) { if (vm.direction == vm.types.entitySearchDirection.from) {
relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); relation.toEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.to.entityType].type);
} else { } else {
relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType)); relation.fromEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.from.entityType].type);
} }
}); });
vm.allRelations = allRelations; vm.allRelations = allRelations;

5
ui/src/app/import-export/import-export.service.js

@ -365,7 +365,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
alias = aliasInfo.aliasName; alias = aliasInfo.aliasName;
filter = { filter = {
type: types.aliasFilterType.entityList.value, type: types.aliasFilterType.entityList.value,
stateEntity: false,
entityType: types.entityType.device, entityType: types.entityType.device,
entityList: [aliasInfo.deviceId], entityList: [aliasInfo.deviceId],
resolveMultiple: false resolveMultiple: false
@ -378,7 +377,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
resolveMultiple: false resolveMultiple: false
} }
if (filter.type == types.aliasFilterType.entityList.value) { if (filter.type == types.aliasFilterType.entityList.value) {
filter.stateEntity = false;
filter.entityList = aliasInfo.deviceFilter.deviceList filter.entityList = aliasInfo.deviceFilter.deviceList
} else { } else {
filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter; filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter;
@ -391,7 +389,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
resolveMultiple: false resolveMultiple: false
} }
if (filter.type == types.aliasFilterType.entityList.value) { if (filter.type == types.aliasFilterType.entityList.value) {
filter.stateEntity = false;
filter.entityList = aliasInfo.entityFilter.entityList; filter.entityList = aliasInfo.entityFilter.entityList;
} else { } else {
filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter; filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter;
@ -662,8 +659,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
entityAliases: missingEntityAliases, entityAliases: missingEntityAliases,
widgets: widgets, widgets: widgets,
isSingleWidget: isSingleWidget, isSingleWidget: isSingleWidget,
isSingleEntityAlias: false,
singleEntityAlias: null,
customTitle: customTitle, customTitle: customTitle,
disableAdd: true disableAdd: true
} }

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

@ -113,23 +113,30 @@ export default angular.module('thingsboard.locale', [])
"alarm-required": "Alarm is required" "alarm-required": "Alarm is required"
}, },
"alias": { "alias": {
"add": "Add alias",
"edit": "Edit alias",
"name": "Alias name",
"name-required": "Alias name is required",
"duplicate-alias": "Alias with same name is already exists.",
"filter-type-entity-list": "Entity list", "filter-type-entity-list": "Entity list",
"filter-type-entity-name": "Entity name", "filter-type-entity-name": "Entity name",
"filter-type-state-entity": "Entity from dashboard state",
"filter-type-state-entity-description": "Entity taken from dashboard state parameters",
"filter-type-asset-type": "Asset type", "filter-type-asset-type": "Asset type",
"filter-type-asset-type-description": "Assets of type '{{assetType}}'",
"filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
"filter-type-device-type": "Device type", "filter-type-device-type": "Device type",
"filter-type-device-type-description": "Devices of type '{{deviceType}}'",
"filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
"filter-type-relations-query": "Relations query", "filter-type-relations-query": "Relations query",
"filter-type-asset-search-query": "Asset search query", "filter-type-asset-search-query": "Asset search query",
"filter-type-device-search-query": "Device search query", "filter-type-device-search-query": "Device search query",
"entity-filter": "Entity filter", "entity-filter": "Entity filter",
"create-entity-filter": "Create entity filter",
"edit-entity-filter": "Edit entity filter",
"entity-filter-required": "Entity filter is required.",
"resolve-multiple": "Resolve as multiple entities", "resolve-multiple": "Resolve as multiple entities",
"filter-type": "Filter type", "filter-type": "Filter type",
"filter-type-required": "Filter type is required.", "filter-type-required": "Filter type is required.",
"use-state-entity": "Use state entity",
"state-entity": "State entity",
"entity-filter-no-entity-matched": "No entities matching specified filter were found.", "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
"no-entity-filter-specified": "No entity filter specified"
}, },
"asset": { "asset": {
"asset": "Asset", "asset": "Asset",
@ -185,7 +192,8 @@ export default angular.module('thingsboard.locale', [])
"idCopiedMessage": "Asset Id has been copied to clipboard", "idCopiedMessage": "Asset Id has been copied to clipboard",
"select-asset": "Select asset", "select-asset": "Select asset",
"no-assets-matching": "No assets matching '{{entity}}' were found.", "no-assets-matching": "No assets matching '{{entity}}' were found.",
"asset-required": "Asset is required" "asset-required": "Asset is required",
"name-starts-with": "Asset name starts with"
}, },
"attribute": { "attribute": {
"attributes": "Attributes", "attributes": "Attributes",
@ -463,7 +471,7 @@ export default angular.module('thingsboard.locale', [])
"alias-required": "Device alias is required.", "alias-required": "Device alias is required.",
"remove-alias": "Remove device alias", "remove-alias": "Remove device alias",
"add-alias": "Add device alias", "add-alias": "Add device alias",
"name-starts-with": "Name starts with", "name-starts-with": "Device name starts with",
"device-list": "Device list", "device-list": "Device list",
"use-device-name-filter": "Use filter", "use-device-name-filter": "Use filter",
"device-list-empty": "No devices selected.", "device-list-empty": "No devices selected.",
@ -549,6 +557,7 @@ export default angular.module('thingsboard.locale', [])
"unable-delete-entity-alias-title": "Unable to delete entity alias", "unable-delete-entity-alias-title": "Unable to delete entity alias",
"unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
"duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.", "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
"missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
"configure-alias": "Configure '{{alias}}' alias", "configure-alias": "Configure '{{alias}}' alias",
"alias": "Alias", "alias": "Alias",
"alias-required": "Entity alias is required.", "alias-required": "Entity alias is required.",
@ -562,24 +571,42 @@ export default angular.module('thingsboard.locale', [])
"entity-name-filter-required": "Entity name filter is required.", "entity-name-filter-required": "Entity name filter is required.",
"entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
"all-subtypes": "All", "all-subtypes": "All",
"select-entities": "Select entities",
"no-aliases-found": "No aliases found.",
"no-alias-matching": "'{{alias}}' not found.",
"create-new-alias": "Create a new one!",
"no-keys-found": "No keys found.",
"no-key-matching": "'{{key}}' not found.",
"create-new-key": "Create a new one!",
"type": "Type", "type": "Type",
"type-required": "Entity type is required.", "type-required": "Entity type is required.",
"type-device": "Device", "type-device": "Device",
"list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
"device-name-starts-with": "Devices whose names start with '{{prefix}}'",
"type-asset": "Asset", "type-asset": "Asset",
"list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
"asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
"type-rule": "Rule", "type-rule": "Rule",
"list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
"rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
"type-plugin": "Plugin", "type-plugin": "Plugin",
"list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
"plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
"type-tenant": "Tenant", "type-tenant": "Tenant",
"list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
"tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
"type-customer": "Customer", "type-customer": "Customer",
"list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
"customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
"type-user": "User", "type-user": "User",
"list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
"user-name-starts-with": "Users whose names start with '{{prefix}}'",
"type-dashboard": "Dashboard", "type-dashboard": "Dashboard",
"list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
"dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
"type-alarm": "Alarm", "type-alarm": "Alarm",
"select-entities": "Select entities", "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
"no-aliases-found": "No aliases found.", "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'"
"no-alias-matching": "'{{alias}}' not found.",
"create-new-alias": "Create a new one!",
"no-keys-found": "No keys found.",
"no-key-matching": "'{{key}}' not found.",
"create-new-key": "Create a new one!"
}, },
"event": { "event": {
"event-type": "Event type", "event-type": "Event type",

Loading…
Cancel
Save