committed by
GitHub
75 changed files with 3732 additions and 1254 deletions
@ -0,0 +1,279 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
|
|||
const varsRegex = /\$\{([^\}]*)\}/g; |
|||
|
|||
export default class AliasController { |
|||
|
|||
constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) { |
|||
this.$scope = $scope; |
|||
this.$q = $q; |
|||
this.$filter = $filter; |
|||
this.utils = utils; |
|||
this.types = types; |
|||
this.entityService = entityService; |
|||
this.stateController = stateController; |
|||
this.entityAliases = angular.copy(entityAliases); |
|||
this.resolvedAliases = {}; |
|||
this.resolvedAliasesPromise = {}; |
|||
this.resolvedAliasesToStateEntities = {}; |
|||
} |
|||
|
|||
updateEntityAliases(newEntityAliases) { |
|||
var changedAliasIds = []; |
|||
for (var aliasId in newEntityAliases) { |
|||
var newEntityAlias = newEntityAliases[aliasId]; |
|||
var prevEntityAlias = this.entityAliases[aliasId]; |
|||
if (!angular.equals(newEntityAlias, prevEntityAlias)) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
for (aliasId in this.entityAliases) { |
|||
if (!newEntityAliases[aliasId]) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
this.entityAliases = angular.copy(newEntityAliases); |
|||
if (changedAliasIds.length) { |
|||
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
|||
} |
|||
} |
|||
|
|||
dashboardStateChanged() { |
|||
var newEntityId = this.stateController.getStateParams().entityId; |
|||
var changedAliasIds = []; |
|||
for (var aliasId in this.resolvedAliasesToStateEntities) { |
|||
var prevEntityId = this.resolvedAliasesToStateEntities[aliasId]; |
|||
if (!angular.equals(newEntityId, prevEntityId)) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
if (changedAliasIds.length) { |
|||
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
|||
} |
|||
} |
|||
|
|||
setAliasUnresolved(aliasId) { |
|||
delete this.resolvedAliases[aliasId]; |
|||
delete this.resolvedAliasesPromise[aliasId]; |
|||
delete this.resolvedAliasesToStateEntities[aliasId]; |
|||
} |
|||
|
|||
getEntityAliases() { |
|||
return this.entityAliases; |
|||
} |
|||
|
|||
getAliasInfo(aliasId) { |
|||
var deferred = this.$q.defer(); |
|||
var aliasInfo = this.resolvedAliases[aliasId]; |
|||
if (aliasInfo) { |
|||
deferred.resolve(aliasInfo); |
|||
return deferred.promise; |
|||
} else if (this.resolvedAliasesPromise[aliasId]) { |
|||
return this.resolvedAliasesPromise[aliasId]; |
|||
} else { |
|||
this.resolvedAliasesPromise[aliasId] = deferred.promise; |
|||
var aliasCtrl = this; |
|||
var entityAlias = this.entityAliases[aliasId]; |
|||
if (entityAlias) { |
|||
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( |
|||
function success(aliasInfo) { |
|||
aliasCtrl.resolvedAliases[aliasId] = aliasInfo; |
|||
if (aliasInfo.stateEntity) { |
|||
aliasCtrl.resolvedAliasesToStateEntities[aliasId] = |
|||
aliasCtrl.stateController.getStateParams().entityId; |
|||
} |
|||
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); |
|||
deferred.resolve(aliasInfo); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
return this.resolvedAliasesPromise[aliasId]; |
|||
} |
|||
} |
|||
|
|||
resolveDatasource(datasource) { |
|||
var deferred = this.$q.defer(); |
|||
if (datasource.type === this.types.datasourceType.entity) { |
|||
if (datasource.entityAliasId) { |
|||
this.getAliasInfo(datasource.entityAliasId).then( |
|||
function success(aliasInfo) { |
|||
datasource.aliasName = aliasInfo.alias; |
|||
if (aliasInfo.resolveMultiple) { |
|||
var newDatasource; |
|||
var resolvedEntities = aliasInfo.resolvedEntities; |
|||
if (resolvedEntities && resolvedEntities.length) { |
|||
var datasources = []; |
|||
for (var i=0;i<resolvedEntities.length;i++) { |
|||
var resolvedEntity = resolvedEntities[i]; |
|||
newDatasource = angular.copy(datasource); |
|||
newDatasource.entityId = resolvedEntity.id; |
|||
newDatasource.entityType = resolvedEntity.entityType; |
|||
newDatasource.entityName = resolvedEntity.name; |
|||
newDatasource.name = resolvedEntity.name; |
|||
newDatasource.generated = i > 0 ? true : false; |
|||
datasources.push(newDatasource); |
|||
} |
|||
deferred.resolve(datasources); |
|||
} else { |
|||
if (aliasInfo.stateEntity) { |
|||
newDatasource = angular.copy(datasource); |
|||
newDatasource.unresolvedStateEntity = true; |
|||
deferred.resolve([newDatasource]); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} |
|||
} else { |
|||
var entity = aliasInfo.currentEntity; |
|||
if (entity) { |
|||
datasource.entityId = entity.id; |
|||
datasource.entityType = entity.entityType; |
|||
datasource.entityName = entity.name; |
|||
datasource.name = entity.name; |
|||
deferred.resolve([datasource]); |
|||
} else { |
|||
if (aliasInfo.stateEntity) { |
|||
datasource.unresolvedStateEntity = true; |
|||
deferred.resolve([datasource]); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { // entityId
|
|||
datasource.aliasName = datasource.entityName; |
|||
datasource.name = datasource.entityName; |
|||
deferred.resolve([datasource]); |
|||
} |
|||
} else { // function
|
|||
deferred.resolve([datasource]); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
resolveDatasources(datasources) { |
|||
|
|||
function updateDataKeyLabel(dataKey, datasource) { |
|||
if (!dataKey.pattern) { |
|||
dataKey.pattern = angular.copy(dataKey.label); |
|||
} |
|||
var pattern = dataKey.pattern; |
|||
var label = dataKey.pattern; |
|||
var match = varsRegex.exec(pattern); |
|||
while (match !== null) { |
|||
var variable = match[0]; |
|||
var variableName = match[1]; |
|||
if (variableName === 'dsName') { |
|||
label = label.split(variable).join(datasource.name); |
|||
} else if (variableName === 'entityName') { |
|||
label = label.split(variable).join(datasource.entityName); |
|||
} else if (variableName === 'deviceName') { |
|||
label = label.split(variable).join(datasource.entityName); |
|||
} else if (variableName === 'aliasName') { |
|||
label = label.split(variable).join(datasource.aliasName); |
|||
} |
|||
match = varsRegex.exec(pattern); |
|||
} |
|||
dataKey.label = label; |
|||
} |
|||
|
|||
function updateDatasourceKeyLabels(datasource) { |
|||
for (var dk = 0; dk < datasource.dataKeys.length; dk++) { |
|||
updateDataKeyLabel(datasource.dataKeys[dk], datasource); |
|||
} |
|||
} |
|||
|
|||
var deferred = this.$q.defer(); |
|||
var newDatasources = angular.copy(datasources); |
|||
var datasorceResolveTasks = []; |
|||
var aliasCtrl = this; |
|||
newDatasources.forEach(function (datasource) { |
|||
var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource); |
|||
datasorceResolveTasks.push(resolveDatasourceTask); |
|||
}); |
|||
this.$q.all(datasorceResolveTasks).then( |
|||
function success(datasourcesArrays) { |
|||
var datasources = [].concat.apply([], datasourcesArrays); |
|||
datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated'); |
|||
var index = 0; |
|||
var functionIndex = 0; |
|||
datasources.forEach(function(datasource) { |
|||
if (datasource.type === aliasCtrl.types.datasourceType.function) { |
|||
var name; |
|||
if (datasource.name && datasource.name.length) { |
|||
name = datasource.name; |
|||
} else { |
|||
functionIndex++; |
|||
name = aliasCtrl.types.datasourceType.function; |
|||
if (functionIndex > 1) { |
|||
name += ' ' + functionIndex; |
|||
} |
|||
} |
|||
datasource.name = name; |
|||
datasource.aliasName = name; |
|||
datasource.entityName = name; |
|||
} else if (datasource.unresolvedStateEntity) { |
|||
datasource.name = "Unresolved"; |
|||
datasource.entityName = "Unresolved"; |
|||
} |
|||
datasource.dataKeys.forEach(function(dataKey) { |
|||
if (datasource.generated) { |
|||
dataKey._hash = Math.random(); |
|||
dataKey.color = aliasCtrl.utils.getMaterialColor(index); |
|||
} |
|||
index++; |
|||
}); |
|||
updateDatasourceKeyLabels(datasource); |
|||
}); |
|||
deferred.resolve(datasources); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
getInstantAliasInfo(aliasId) { |
|||
return this.resolvedAliases[aliasId]; |
|||
} |
|||
|
|||
updateCurrentAliasEntity(aliasId, currentEntity) { |
|||
var aliasInfo = this.resolvedAliases[aliasId]; |
|||
if (aliasInfo) { |
|||
var prevCurrentEntity = aliasInfo.currentEntity; |
|||
if (!angular.equals(currentEntity, prevCurrentEntity)) { |
|||
aliasInfo.currentEntity = currentEntity; |
|||
this.$scope.$broadcast('entityAliasesChanged', [aliasId]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) { |
|||
|
|||
var vm = this; |
|||
vm._mdPanelRef = mdPanelRef; |
|||
vm.aliasController = aliasController; |
|||
vm.onEntityAliasesUpdate = onEntityAliasesUpdate; |
|||
vm.entityAliases = {}; |
|||
vm.entityAliasesInfo = {}; |
|||
|
|||
vm.currentAliasEntityChanged = currentAliasEntityChanged; |
|||
|
|||
var allEntityAliases = vm.aliasController.getEntityAliases(); |
|||
for (var aliasId in allEntityAliases) { |
|||
var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); |
|||
if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { |
|||
vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); |
|||
vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; |
|||
} |
|||
} |
|||
|
|||
function currentAliasEntityChanged(aliasId, selectedId) { |
|||
var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities; |
|||
var selected = $filter('filter')(resolvedEntities, {id: selectedId}); |
|||
if (selected && selected.length) { |
|||
vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]); |
|||
if (onEntityAliasesUpdate) { |
|||
onEntityAliasesUpdate(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
/* |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
import './entity-alias-dialog.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function EntityAliasDialogController($scope, $mdDialog, $q, $filter, utils, entityService, types, isAdd, allowedEntityTypes, entityAliases, alias) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.types = types; |
|||
vm.isAdd = isAdd; |
|||
vm.allowedEntityTypes = allowedEntityTypes; |
|||
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.save = save; |
|||
|
|||
$scope.$watch('vm.alias.alias', function (newAlias) { |
|||
if (newAlias) { |
|||
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); |
|||
} |
|||
}); |
|||
|
|||
$scope.$watch('theForm.$pristine', function() { |
|||
if ($scope.theForm && !$scope.theForm.$pristine) { |
|||
$scope.theForm.$setValidity('entityFilter', true); |
|||
} |
|||
}); |
|||
|
|||
function validate() { |
|||
var deferred = $q.defer(); |
|||
var validationResult = { |
|||
entity: null, |
|||
stateEntity: false |
|||
} |
|||
entityService.resolveAliasFilter(vm.alias.filter, null, 1).then( |
|||
function success(result) { |
|||
validationResult.stateEntity = result.stateEntity; |
|||
var entities = result.entities; |
|||
if (entities.length) { |
|||
validationResult.entity = entities[0]; |
|||
} |
|||
deferred.resolve(validationResult); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function save() { |
|||
$scope.theForm.$setPristine(); |
|||
validate().then( |
|||
function success() { |
|||
if (vm.isAdd) { |
|||
vm.alias.id = utils.guid(); |
|||
} |
|||
$mdDialog.hide(vm.alias); |
|||
}, |
|||
function fail() { |
|||
$scope.theForm.$setValidity('entityFilter', false); |
|||
} |
|||
) |
|||
} |
|||
|
|||
} |
|||
@ -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> |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* 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-aliases-dialog { |
|||
.md-dialog-content { |
|||
padding-bottom: 0px; |
|||
padding-top: 0px; |
|||
} |
|||
.tb-aliases-header { |
|||
min-height: 40px; |
|||
padding: 0 34px 0 34px; |
|||
margin: 5px; |
|||
.tb-header-label { |
|||
font-size: 14px; |
|||
color: rgba(0, 0, 0, 0.570588); |
|||
} |
|||
} |
|||
.tb-alias { |
|||
padding: 0 0 0 10px; |
|||
margin: 5px; |
|||
md-input-container { |
|||
margin: 0px; |
|||
} |
|||
.tb-resolve-multiple-switch { |
|||
padding-left: 10px; |
|||
.resolve-multiple-switch { |
|||
margin: 0; |
|||
} |
|||
} |
|||
.md-button { |
|||
&.md-icon-button { |
|||
margin: 0px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
<!-- |
|||
|
|||
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-aliases-dialog" style="width: 700px;" aria-label="{{ vm.title | translate }}"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2>{{ vm.title | 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> |
|||
<div class="tb-aliases-header" flex layout="row" layout-align="start center"> |
|||
<span flex="5"></span> |
|||
<div flex layout="row" layout-align="start center"> |
|||
<span class="tb-header-label" translate flex="20" style="min-width: 150px;">alias.name</span> |
|||
<span class="tb-header-label" translate flex="70" style="padding-left: 10px;">alias.entity-filter</span> |
|||
<span class="tb-header-label" translate flex="10" style="padding-left: 10px; min-width: 120px;">alias.resolve-multiple</span> |
|||
<span style="min-width: 80px;"></span> |
|||
</div> |
|||
</div> |
|||
<md-divider></md-divider> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset ng-disabled="loading"> |
|||
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> |
|||
<span flex="5">{{$index + 1}}.</span> |
|||
<di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> |
|||
<md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block"> |
|||
<input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> |
|||
<div ng-messages="aliasForm.alias.$error"> |
|||
<div translate ng-message="required">entity.alias-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<tb-entity-filter-view |
|||
flex="70" style="padding-left: 10px;" |
|||
ng-model="entityAlias.filter"> |
|||
</tb-entity-filter-view> |
|||
<section flex="10" style="padding-left: 10px; min-width: 120px;" |
|||
class="tb-resolve-multiple-switch" |
|||
layout="column" |
|||
layout-align="center center"> |
|||
<md-switch class="resolve-multiple-switch" |
|||
ng-model="entityAlias.filter.resolveMultiple" |
|||
aria-label="resolve-multiple-switcher"> |
|||
</md-switch> |
|||
</section> |
|||
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" |
|||
ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'alias.edit' | translate }} |
|||
</md-tooltip> |
|||
<md-icon aria-label="{{ 'alias.edit' | translate }}" class="material-icons"> |
|||
edit |
|||
</md-icon> |
|||
</md-button> |
|||
<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 }}"> |
|||
<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> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" |
|||
ng-click="vm.addAlias($event)" |
|||
aria-label="{{ 'alias.add' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'alias.add' | translate }} |
|||
</md-tooltip> |
|||
<span translate>alias.add</span> |
|||
</md-button> |
|||
<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> |
|||
@ -1,106 +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-aliases-dialog" style="width: 700px;" aria-label="{{ vm.title | translate }}"> |
|||
<form name="theForm" ng-submit="vm.save()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2>{{ vm.isSingleEntityAlias ? ('entity.configure-alias' | translate:vm.singleEntityAlias ) : (vm.title | 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 ng-show="vm.isSingleEntityAlias" layout="row"> |
|||
<tb-entity-type-select style="min-width: 100px;" |
|||
ng-model="vm.singleEntityAlias.entityType" |
|||
allowed-entity-types="vm.allowedEntityTypes"> |
|||
</tb-entity-type-select> |
|||
<tb-entity-filter flex entity-type="vm.singleEntityAlias.entityType" ng-model="vm.singleEntityAlias.entityFilter"> |
|||
</tb-entity-filter> |
|||
</div> |
|||
<div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> |
|||
<span flex="5"></span> |
|||
<div flex layout="row" layout-align="start center" |
|||
style="padding: 0 0 0 10px; margin: 5px;"> |
|||
<span translate flex="20" style="min-width: 100px;">entity.alias</span> |
|||
<span translate flex="20" style="min-width: 100px;">entity.type</span> |
|||
<span translate flex="60" style="min-width: 190px; padding-left: 10px;">entity.entities</span> |
|||
<span style="min-width: 40px;"></span> |
|||
</div> |
|||
</div> |
|||
<div ng-show="!vm.isSingleEntityAlias" style="max-height: 500px; overflow: auto; padding-bottom: 20px;"> |
|||
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> |
|||
<span flex="5">{{$index + 1}}.</span> |
|||
<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> |
|||
<md-input-container flex="20" style="min-width: 100px;" md-no-float class="md-block"> |
|||
<input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> |
|||
<div ng-messages="aliasForm.alias.$error"> |
|||
<div translate ng-message="required">entity.alias-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<section flex="20" layout="column" style="min-width: 100px;" > |
|||
<tb-entity-type-select hide-label style="padding-left: 10px;" |
|||
ng-model="entityAlias.entityType" |
|||
allowed-entity-types="vm.allowedEntityTypes"> |
|||
</tb-entity-type-select> |
|||
</section> |
|||
<section flex="60" layout="column"> |
|||
<tb-entity-filter style="padding-left: 10px;" |
|||
entity-type="entityAlias.entityType" |
|||
ng-model="entityAlias.entityFilter" |
|||
on-matching-entity-change="vm.onFilterEntityChanged(entity, entityAlias)"> |
|||
</tb-entity-filter> |
|||
</section> |
|||
<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 }}"> |
|||
<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> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" style="padding-bottom: 10px;"> |
|||
<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'entity.add-alias' | translate }} |
|||
</md-tooltip> |
|||
<span translate>action.add</span> |
|||
</md-button> |
|||
</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> |
|||
@ -0,0 +1,205 @@ |
|||
/* |
|||
* 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; |
|||
case types.aliasFilterType.relationsQuery.value: |
|||
var rootEntityText; |
|||
var directionText; |
|||
var allEntitiesText = $translate.instant('alias.all-entities'); |
|||
var anyRelationText = $translate.instant('alias.any-relation'); |
|||
if (scope.filter.rootStateEntity) { |
|||
rootEntityText = $translate.instant('alias.state-entity'); |
|||
} else { |
|||
rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type); |
|||
} |
|||
directionText = $translate.instant('relation.direction-type.' + scope.filter.direction); |
|||
var relationFilters = scope.filter.filters; |
|||
if (relationFilters && relationFilters.length) { |
|||
var relationFiltersDisplayValues = []; |
|||
relationFilters.forEach(function(relationFilter) { |
|||
var entitiesText; |
|||
if (relationFilter.entityTypes && relationFilter.entityTypes.length) { |
|||
var entitiesNamesList = []; |
|||
relationFilter.entityTypes.forEach(function(entityType) { |
|||
entitiesNamesList.push( |
|||
$translate.instant(types.entityTypeTranslations[entityType].typePlural) |
|||
); |
|||
}); |
|||
entitiesText = entitiesNamesList.join(', '); |
|||
} else { |
|||
entitiesText = allEntitiesText; |
|||
} |
|||
var relationTypeText; |
|||
if (relationFilter.relationType && relationFilter.relationType.length) { |
|||
relationTypeText = "'" + relationFilter.relationType + "'"; |
|||
} else { |
|||
relationTypeText = anyRelationText; |
|||
} |
|||
var relationFilterDisplayValue = $translate.instant('alias.filter-type-relations-query-description', |
|||
{ |
|||
entities: entitiesText, |
|||
relationType: relationTypeText, |
|||
direction: directionText, |
|||
rootEntity: rootEntityText |
|||
} |
|||
); |
|||
relationFiltersDisplayValues.push(relationFilterDisplayValue); |
|||
}); |
|||
scope.filterDisplayValue = relationFiltersDisplayValues.join(', '); |
|||
} else { |
|||
scope.filterDisplayValue = $translate.instant('alias.filter-type-relations-query-description', |
|||
{ |
|||
entities: allEntitiesText, |
|||
relationType: anyRelationText, |
|||
direction: directionText, |
|||
rootEntity: rootEntityText |
|||
} |
|||
); |
|||
} |
|||
break; |
|||
case types.aliasFilterType.assetSearchQuery.value: |
|||
case types.aliasFilterType.deviceSearchQuery.value: |
|||
allEntitiesText = $translate.instant('alias.all-entities'); |
|||
anyRelationText = $translate.instant('alias.any-relation'); |
|||
if (scope.filter.rootStateEntity) { |
|||
rootEntityText = $translate.instant('alias.state-entity'); |
|||
} else { |
|||
rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type); |
|||
} |
|||
directionText = $translate.instant('relation.direction-type.' + scope.filter.direction); |
|||
var relationTypeText; |
|||
if (scope.filter.relationType && scope.filter.relationType.length) { |
|||
relationTypeText = "'" + scope.filter.relationType + "'"; |
|||
} else { |
|||
relationTypeText = anyRelationText; |
|||
} |
|||
|
|||
var translationValues = { |
|||
relationType: relationTypeText, |
|||
direction: directionText, |
|||
rootEntity: rootEntityText |
|||
} |
|||
|
|||
if (scope.filter.type == types.aliasFilterType.assetSearchQuery.value) { |
|||
var assetTypesQuoted = []; |
|||
scope.filter.assetTypes.forEach(function(assetType) { |
|||
assetTypesQuoted.push("'"+assetType+"'"); |
|||
}); |
|||
var assetTypesText = assetTypesQuoted.join(', '); |
|||
translationValues.assetTypes = assetTypesText; |
|||
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description', |
|||
translationValues |
|||
); |
|||
} else { |
|||
var deviceTypesQuoted = []; |
|||
scope.filter.deviceTypes.forEach(function(deviceType) { |
|||
deviceTypesQuoted.push("'"+deviceType+"'"); |
|||
}); |
|||
var deviceTypesText = deviceTypesQuoted.join(', '); |
|||
translationValues.deviceTypes = deviceTypesText; |
|||
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description', |
|||
translationValues |
|||
); |
|||
} |
|||
break; |
|||
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 |
|||
}; |
|||
|
|||
} |
|||
@ -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> |
|||
@ -0,0 +1,130 @@ |
|||
/* |
|||
* 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 entityListTemplate from './entity-list.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
import './entity-list.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function EntityListDirective($compile, $templateCache, $q, $mdUtil, entityService) { |
|||
|
|||
var linker = function (scope, element, attrs, ngModelCtrl) { |
|||
|
|||
var template = $templateCache.get(entityListTemplate); |
|||
element.html(template); |
|||
|
|||
scope.ngModelCtrl = ngModelCtrl; |
|||
|
|||
scope.$watch('tbRequired', function () { |
|||
scope.updateValidity(); |
|||
}); |
|||
|
|||
scope.fetchEntities = function(searchText, limit) { |
|||
var deferred = $q.defer(); |
|||
entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then( |
|||
function success(result) { |
|||
if (result) { |
|||
deferred.resolve(result); |
|||
} else { |
|||
deferred.resolve([]); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
scope.updateValidity = function() { |
|||
var value = ngModelCtrl.$viewValue; |
|||
var valid = !scope.tbRequired || value && value.length > 0; |
|||
ngModelCtrl.$setValidity('entityList', valid); |
|||
} |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
destroyWatchers(); |
|||
var value = ngModelCtrl.$viewValue; |
|||
scope.entityList = []; |
|||
if (value && value.length > 0) { |
|||
entityService.getEntities(scope.entityType, value).then(function (entities) { |
|||
scope.entityList = entities; |
|||
initWatchers(); |
|||
}); |
|||
} else { |
|||
initWatchers(); |
|||
} |
|||
} |
|||
|
|||
function initWatchers() { |
|||
scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) { |
|||
if (!angular.equals(newEntityType, prevEntityType)) { |
|||
scope.entityList = []; |
|||
} |
|||
}); |
|||
scope.entityListDeregistration = scope.$watch('entityList', function () { |
|||
var ids = []; |
|||
if (scope.entityList && scope.entityList.length > 0) { |
|||
for (var i=0;i<scope.entityList.length;i++) { |
|||
ids.push(scope.entityList[i].id.id); |
|||
} |
|||
} |
|||
var value = ngModelCtrl.$viewValue; |
|||
if (!angular.equals(ids, value)) { |
|||
ngModelCtrl.$setViewValue(ids); |
|||
} |
|||
scope.updateValidity(); |
|||
}, true); |
|||
} |
|||
|
|||
function destroyWatchers() { |
|||
if (scope.entityTypeDeregistration) { |
|||
scope.entityTypeDeregistration(); |
|||
scope.entityTypeDeregistration = null; |
|||
} |
|||
if (scope.entityListDeregistration) { |
|||
scope.entityListDeregistration(); |
|||
scope.entityListDeregistration = null; |
|||
} |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
|
|||
$mdUtil.nextTick(function(){ |
|||
var inputElement = angular.element('input', element); |
|||
inputElement.on('blur', function() { |
|||
scope.inputTouched = true; |
|||
} ); |
|||
}); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
link: linker, |
|||
scope: { |
|||
disabled:'=ngDisabled', |
|||
tbRequired: '=?', |
|||
entityType: '=' |
|||
} |
|||
}; |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* 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-list { |
|||
#entity_list_chips { |
|||
.md-chips { |
|||
padding-bottom: 1px; |
|||
} |
|||
} |
|||
.tb-error-messages { |
|||
margin-top: -11px; |
|||
height: 35px; |
|||
.tb-error-message { |
|||
padding-left: 1px; |
|||
} |
|||
} |
|||
}*/ |
|||
@ -0,0 +1,52 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
|
|||
<section flex layout='column' class="tb-entity-list"> |
|||
<md-chips flex |
|||
readonly="disabled" |
|||
id="entity_list_chips" |
|||
ng-required="tbRequired" |
|||
ng-model="entityList" |
|||
md-autocomplete-snap |
|||
md-require-match="true"> |
|||
<md-autocomplete |
|||
md-no-cache="true" |
|||
id="entity" |
|||
md-selected-item="selectedEntity" |
|||
md-search-text="entitySearchText" |
|||
md-items="item in fetchEntities(entitySearchText, 10)" |
|||
md-item-text="item.name" |
|||
md-min-length="0" |
|||
placeholder="{{ 'entity.entity-list' | translate }}"> |
|||
<md-item-template> |
|||
<span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span> |
|||
</md-item-template> |
|||
<md-not-found> |
|||
<span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span> |
|||
</md-not-found> |
|||
</md-autocomplete> |
|||
<md-chip-template> |
|||
<span> |
|||
<strong>{{$chip.name}}</strong> |
|||
</span> |
|||
</md-chip-template> |
|||
</md-chips> |
|||
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched" role="alert"> |
|||
<div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div> |
|||
</div> |
|||
</section> |
|||
@ -0,0 +1,146 @@ |
|||
/* |
|||
* 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 entitySubtypeListTemplate from './entity-subtype-list.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
import './entity-subtype-list.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService) { |
|||
|
|||
var linker = function (scope, element, attrs, ngModelCtrl) { |
|||
|
|||
var template = $templateCache.get(entitySubtypeListTemplate); |
|||
element.html(template); |
|||
|
|||
scope.ngModelCtrl = ngModelCtrl; |
|||
|
|||
|
|||
scope.entitySubtypesList = []; |
|||
scope.entitySubtypes = null; |
|||
|
|||
if (scope.entityType == types.entityType.asset) { |
|||
scope.placeholder = scope.tbRequired ? $translate.instant('asset.enter-asset-type') |
|||
: $translate.instant('asset.any-asset'); |
|||
scope.secondaryPlaceholder = '+' + $translate.instant('asset.asset-type'); |
|||
scope.noSubtypesMathingText = 'asset.no-asset-types-matching'; |
|||
scope.subtypeListEmptyText = 'asset.asset-type-list-empty'; |
|||
} else if (scope.entityType == types.entityType.device) { |
|||
scope.placeholder = scope.tbRequired ? $translate.instant('device.enter-device-type') |
|||
: $translate.instant('device.any-device'); |
|||
scope.secondaryPlaceholder = '+' + $translate.instant('device.device-type'); |
|||
scope.noSubtypesMathingText = 'device.no-device-types-matching'; |
|||
scope.subtypeListEmptyText = 'device.device-type-list-empty'; |
|||
} |
|||
|
|||
scope.$watch('tbRequired', function () { |
|||
scope.updateValidity(); |
|||
}); |
|||
|
|||
scope.fetchEntitySubtypes = function(searchText) { |
|||
var deferred = $q.defer(); |
|||
loadSubTypes().then( |
|||
function success(subTypes) { |
|||
var result = $filter('filter')(subTypes, {'$': searchText}); |
|||
if (result && result.length) { |
|||
deferred.resolve(result); |
|||
} else { |
|||
deferred.resolve([searchText]); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
scope.updateValidity = function() { |
|||
var value = ngModelCtrl.$viewValue; |
|||
var valid = !scope.tbRequired || value && value.length > 0; |
|||
ngModelCtrl.$setValidity('entitySubtypesList', valid); |
|||
} |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
scope.entitySubtypesList = ngModelCtrl.$viewValue; |
|||
if (!scope.entitySubtypesList) { |
|||
scope.entitySubtypesList = []; |
|||
} |
|||
} |
|||
|
|||
scope.$watch('entitySubtypesList', function () { |
|||
ngModelCtrl.$setViewValue(scope.entitySubtypesList); |
|||
scope.updateValidity(); |
|||
}, true); |
|||
|
|||
function loadSubTypes() { |
|||
var deferred = $q.defer(); |
|||
if (!scope.entitySubtypes) { |
|||
var entitySubtypesPromise; |
|||
if (scope.entityType == types.entityType.asset) { |
|||
entitySubtypesPromise = assetService.getAssetTypes(); |
|||
} else if (scope.entityType == types.entityType.device) { |
|||
entitySubtypesPromise = deviceService.getDeviceTypes(); |
|||
} |
|||
if (entitySubtypesPromise) { |
|||
entitySubtypesPromise.then( |
|||
function success(types) { |
|||
scope.entitySubtypes = []; |
|||
types.forEach(function (type) { |
|||
scope.entitySubtypes.push(type.type); |
|||
}); |
|||
deferred.resolve(scope.entitySubtypes); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} else { |
|||
deferred.resolve(scope.entitySubtypes); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
|
|||
$mdUtil.nextTick(function(){ |
|||
var inputElement = angular.element('input', element); |
|||
inputElement.on('blur', function() { |
|||
scope.inputTouched = true; |
|||
} ); |
|||
}); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
link: linker, |
|||
scope: { |
|||
disabled:'=ngDisabled', |
|||
tbRequired: '=?', |
|||
entityType: "=" |
|||
} |
|||
}; |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* 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-subtype-list { |
|||
#entity_subtype_list_chips { |
|||
.md-chips { |
|||
padding-bottom: 1px; |
|||
} |
|||
} |
|||
.tb-error-messages { |
|||
margin-top: -11px; |
|||
height: 35px; |
|||
.tb-error-message { |
|||
padding-left: 1px; |
|||
} |
|||
} |
|||
}*/ |
|||
@ -0,0 +1,54 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
|
|||
<section flex layout='column' class="tb-entity-subtype-list"> |
|||
<md-chips flex |
|||
readonly="disabled" |
|||
id="entity_subtype_list_chips" |
|||
ng-required="tbRequired" |
|||
ng-model="entitySubtypesList" |
|||
placeholder="{{placeholder}}" |
|||
secondary-placeholder="{{secondaryPlaceholder}}" |
|||
md-autocomplete-snap |
|||
md-require-match="false"> |
|||
<md-autocomplete |
|||
md-no-cache="true" |
|||
id="entitySubtype" |
|||
md-selected-item="selectedEntitySubtype" |
|||
md-search-text="entitySubtypeSearchText" |
|||
md-items="item in fetchEntitySubtypes(entitySubtypeSearchText)" |
|||
md-item-text="item" |
|||
md-min-length="0" |
|||
placeholder="{{ (!entitySubtypesList || !entitySubtypesList.length) ? placeholder : secondaryPlaceholder }}"> |
|||
<md-item-template> |
|||
<span md-highlight-text="entitySubtypeSearchText" md-highlight-flags="^i">{{item}}</span> |
|||
</md-item-template> |
|||
<md-not-found> |
|||
<span translate translate-values='{ entitySubtype: entitySubtypeSearchText }'>{{noSubtypesMathingText}}</span> |
|||
</md-not-found> |
|||
</md-autocomplete> |
|||
<md-chip-template> |
|||
<span> |
|||
<strong>{{$chip}}</strong> |
|||
</span> |
|||
</md-chip-template> |
|||
</md-chips> |
|||
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched && tbRequired" role="alert"> |
|||
<div translate ng-message="entitySubtypesList" class="tb-error-message">{{subtypeListEmptyText}}</div> |
|||
</div> |
|||
</section> |
|||
@ -0,0 +1,111 @@ |
|||
/* |
|||
* 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 entityTypeListTemplate from './entity-type-list.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
import './entity-type-list.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function EntityTypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, entityService) { |
|||
|
|||
var linker = function (scope, element, attrs, ngModelCtrl) { |
|||
|
|||
var template = $templateCache.get(entityTypeListTemplate); |
|||
element.html(template); |
|||
|
|||
scope.ngModelCtrl = ngModelCtrl; |
|||
|
|||
scope.placeholder = scope.tbRequired ? $translate.instant('entity.enter-entity-type') |
|||
: $translate.instant('entity.any-entity'); |
|||
scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type'); |
|||
|
|||
var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); |
|||
scope.entityTypesList = []; |
|||
for (var type in entityTypes) { |
|||
var entityTypeInfo = {}; |
|||
entityTypeInfo.value = entityTypes[type]; |
|||
entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; |
|||
scope.entityTypesList.push(entityTypeInfo); |
|||
} |
|||
|
|||
scope.$watch('tbRequired', function () { |
|||
scope.updateValidity(); |
|||
}); |
|||
|
|||
scope.fetchEntityTypes = function(searchText) { |
|||
var deferred = $q.defer(); |
|||
var entityTypes = $filter('filter')(scope.entityTypesList, {name: searchText}); |
|||
deferred.resolve(entityTypes); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
scope.updateValidity = function() { |
|||
var value = ngModelCtrl.$viewValue; |
|||
var valid = !scope.tbRequired || value && value.length > 0; |
|||
ngModelCtrl.$setValidity('entityTypeList', valid); |
|||
} |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
scope.entityTypeList = []; |
|||
var value = ngModelCtrl.$viewValue; |
|||
if (value && value.length) { |
|||
value.forEach(function(type) { |
|||
var entityTypeInfo = {}; |
|||
entityTypeInfo.value = type; |
|||
entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; |
|||
scope.entityTypeList.push(entityTypeInfo); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
scope.$watch('entityTypeList', function () { |
|||
var values = []; |
|||
if (scope.entityTypeList && scope.entityTypeList.length) { |
|||
scope.entityTypeList.forEach(function(entityType) { |
|||
values.push(entityType.value); |
|||
}); |
|||
} |
|||
ngModelCtrl.$setViewValue(values); |
|||
scope.updateValidity(); |
|||
}, true); |
|||
|
|||
$compile(element.contents())(scope); |
|||
|
|||
$mdUtil.nextTick(function(){ |
|||
var inputElement = angular.element('input', element); |
|||
inputElement.on('blur', function() { |
|||
scope.inputTouched = true; |
|||
} ); |
|||
}); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
link: linker, |
|||
scope: { |
|||
disabled:'=ngDisabled', |
|||
tbRequired: '=?', |
|||
allowedEntityTypes: '=?' |
|||
} |
|||
}; |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* 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-type-list { |
|||
#entity_type_list_chips { |
|||
.md-chips { |
|||
padding-bottom: 1px; |
|||
} |
|||
} |
|||
.tb-error-messages { |
|||
margin-top: -11px; |
|||
height: 35px; |
|||
.tb-error-message { |
|||
padding-left: 1px; |
|||
} |
|||
} |
|||
}*/ |
|||
@ -0,0 +1,54 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
|
|||
<section flex layout='column' class="tb-entity-type-list"> |
|||
<md-chips flex |
|||
readonly="disabled" |
|||
id="entity_type_list_chips" |
|||
ng-required="tbRequired" |
|||
ng-model="entityTypeList" |
|||
placeholder="{{placeholder}}" |
|||
secondary-placeholder="{{secondaryPlaceholder}}" |
|||
md-autocomplete-snap |
|||
md-require-match="true"> |
|||
<md-autocomplete |
|||
md-no-cache="true" |
|||
id="entityType" |
|||
md-selected-item="selectedEntityType" |
|||
md-search-text="entityTypeSearchText" |
|||
md-items="item in fetchEntityTypes(entityTypeSearchText)" |
|||
md-item-text="item.name" |
|||
md-min-length="0" |
|||
placeholder="{{ (!entityTypeList || !entityTypeList.length) ? placeholder : secondaryPlaceholder }}"> |
|||
<md-item-template> |
|||
<span md-highlight-text="entityTypeSearchText" md-highlight-flags="^i">{{item.name}}</span> |
|||
</md-item-template> |
|||
<md-not-found> |
|||
<span translate translate-values='{ entityType: entityTypeSearchText }'>entity.no-entity-types-matching</span> |
|||
</md-not-found> |
|||
</md-autocomplete> |
|||
<md-chip-template> |
|||
<span> |
|||
<strong>{{$chip.name}}</strong> |
|||
</span> |
|||
</md-chip-template> |
|||
</md-chips> |
|||
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched && tbRequired" role="alert"> |
|||
<div translate ng-message="entityTypeList" class="tb-error-message">entity.entity-type-list-empty</div> |
|||
</div> |
|||
</section> |
|||
@ -0,0 +1,85 @@ |
|||
/* |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
import './relation-filters.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import relationFiltersTemplate from './relation-filters.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function RelationFilters($compile, $templateCache) { |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
scope: { |
|||
allowedEntityTypes: '=?' |
|||
}, |
|||
link: linker |
|||
}; |
|||
|
|||
function linker( scope, element, attrs, ngModelCtrl ) { |
|||
|
|||
var template = $templateCache.get(relationFiltersTemplate); |
|||
element.html(template); |
|||
|
|||
scope.relationFilters = []; |
|||
|
|||
scope.addFilter = addFilter; |
|||
scope.removeFilter = removeFilter; |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
if (ngModelCtrl.$viewValue) { |
|||
var value = ngModelCtrl.$viewValue; |
|||
value.forEach(function (filter) { |
|||
scope.relationFilters.push(filter); |
|||
}); |
|||
} |
|||
scope.$watch('relationFilters', function (newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
updateValue(); |
|||
} |
|||
}, true); |
|||
} |
|||
|
|||
function addFilter() { |
|||
var filter = { |
|||
relationType: null, |
|||
entityTypes: [] |
|||
}; |
|||
scope.relationFilters.push(filter); |
|||
} |
|||
|
|||
function removeFilter($event, filter) { |
|||
var index = scope.relationFilters.indexOf(filter); |
|||
if (index > -1) { |
|||
scope.relationFilters.splice(index, 1); |
|||
} |
|||
} |
|||
|
|||
function updateValue() { |
|||
var value = []; |
|||
scope.relationFilters.forEach(function (filter) { |
|||
value.push(filter); |
|||
}); |
|||
ngModelCtrl.$setViewValue(value); |
|||
} |
|||
$compile(element.contents())(scope); |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
/** |
|||
* Copyright © 2016-2017 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
.tb-relation-filters { |
|||
.header { |
|||
padding-left: 5px; |
|||
padding-right: 5px; |
|||
padding-bottom: 5px; |
|||
.cell { |
|||
padding-left: 5px; |
|||
padding-right: 5px; |
|||
color: rgba(0,0,0,.54); |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
white-space: nowrap; |
|||
} |
|||
} |
|||
.body { |
|||
padding-left: 5px; |
|||
padding-right: 5px; |
|||
max-height: 300px; |
|||
overflow: auto; |
|||
padding-bottom: 20px; |
|||
.row { |
|||
padding-top: 5px; |
|||
} |
|||
.cell { |
|||
padding-left: 5px; |
|||
padding-right: 5px; |
|||
|
|||
md-select { |
|||
margin: 0 0 24px; |
|||
} |
|||
|
|||
md-input-container { |
|||
margin: 0; |
|||
} |
|||
|
|||
md-chips-wrap { |
|||
padding: 0px; |
|||
margin: 0 0 24px; |
|||
.md-chip-input-container { |
|||
margin: 0; |
|||
} |
|||
md-autocomplete { |
|||
height: 30px; |
|||
md-autocomplete-wrap { |
|||
height: 30px; |
|||
} |
|||
} |
|||
} |
|||
.md-chips .md-chip-input-container input { |
|||
padding: 2px 2px 2px; |
|||
height: 26px; |
|||
line-height: 26px; |
|||
} |
|||
|
|||
} |
|||
|
|||
.md-button { |
|||
margin: 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
<!-- |
|||
|
|||
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 class="tb-relation-filters"> |
|||
<div class="header" ng-show="relationFilters.length"> |
|||
<div layout="row" layout-align="start center"> |
|||
<span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span> |
|||
<span class="cell" flex translate>entity.entity-types</span> |
|||
<span class="cell" style="width: 40px; min-width: 40px;"> </span> |
|||
</div> |
|||
</div> |
|||
<div class="body" ng-show="relationFilters.length"> |
|||
<div class="row" ng-form name="relationFilterForm" flex layout="row" layout-align="start center" ng-repeat="filter in relationFilters track by $index"> |
|||
<div class="md-whiteframe-1dp" flex layout="row" layout-align="start center"> |
|||
<tb-relation-type-autocomplete class="cell" style="width: 200px; min-width: 200px;" |
|||
hide-label |
|||
the-form="relationFilterForm" |
|||
ng-model="filter.relationType" |
|||
tb-required="false"> |
|||
</tb-relation-type-autocomplete> |
|||
<tb-entity-type-list class="cell" flex |
|||
ng-model="filter.entityTypes" |
|||
allowed-entity-types="allowedEntityTypes" |
|||
tb-required="false"> |
|||
</tb-entity-type-list> |
|||
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;" |
|||
ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'relation.remove-relation-filter' | translate }} |
|||
</md-tooltip> |
|||
<md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons"> |
|||
close |
|||
</md-icon> |
|||
</md-button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="any-filter" ng-show="!relationFilters.length"> |
|||
<span layout-align="center center" |
|||
class="tb-prompt" translate>relation.any-relation</span> |
|||
</div> |
|||
<div> |
|||
<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'relation.add-relation-filter' | translate }} |
|||
</md-tooltip> |
|||
<md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons"> |
|||
add |
|||
</md-icon> |
|||
{{ 'action.add' | translate }} |
|||
</md-button> |
|||
</div> |
|||
</div> |
|||
Loading…
Reference in new issue