121 changed files with 4896 additions and 1342 deletions
@ -0,0 +1,58 @@ |
|||||
|
/** |
||||
|
* 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. |
||||
|
*/ |
||||
|
package org.thingsboard.server.common.data.alarm; |
||||
|
|
||||
|
public class AlarmInfo extends Alarm { |
||||
|
|
||||
|
private static final long serialVersionUID = 2807343093519543363L; |
||||
|
|
||||
|
private String originatorName; |
||||
|
|
||||
|
public AlarmInfo() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public AlarmInfo(Alarm alarm) { |
||||
|
super(alarm); |
||||
|
} |
||||
|
|
||||
|
public String getOriginatorName() { |
||||
|
return originatorName; |
||||
|
} |
||||
|
|
||||
|
public void setOriginatorName(String originatorName) { |
||||
|
this.originatorName = originatorName; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean equals(Object o) { |
||||
|
if (this == o) return true; |
||||
|
if (o == null || getClass() != o.getClass()) return false; |
||||
|
if (!super.equals(o)) return false; |
||||
|
|
||||
|
AlarmInfo alarmInfo = (AlarmInfo) o; |
||||
|
|
||||
|
return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int hashCode() { |
||||
|
int result = super.hashCode(); |
||||
|
result = 31 * result + (originatorName != null ? originatorName.hashCode() : 0); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,134 @@ |
|||||
|
/* |
||||
|
* 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 'brace/ext/language_tools'; |
||||
|
import 'brace/mode/json'; |
||||
|
import 'brace/theme/github'; |
||||
|
import beautify from 'js-beautify'; |
||||
|
|
||||
|
import './alarm-details-dialog.scss'; |
||||
|
|
||||
|
const js_beautify = beautify.js; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) { |
||||
|
|
||||
|
var vm = this; |
||||
|
|
||||
|
vm.alarmId = alarmId; |
||||
|
vm.types = types; |
||||
|
vm.alarm = null; |
||||
|
|
||||
|
vm.alarmUpdated = false; |
||||
|
|
||||
|
showingCallback.onShowing = function(scope, element) { |
||||
|
updateEditorSize(element); |
||||
|
} |
||||
|
|
||||
|
vm.alarmDetailsOptions = { |
||||
|
useWrapMode: false, |
||||
|
mode: 'json', |
||||
|
showGutter: false, |
||||
|
showPrintMargin: false, |
||||
|
theme: 'github', |
||||
|
advanced: { |
||||
|
enableSnippets: false, |
||||
|
enableBasicAutocompletion: false, |
||||
|
enableLiveAutocompletion: false |
||||
|
}, |
||||
|
onLoad: function (_ace) { |
||||
|
vm.editor = _ace; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
vm.close = close; |
||||
|
vm.acknowledge = acknowledge; |
||||
|
vm.clear = clear; |
||||
|
|
||||
|
loadAlarm(); |
||||
|
|
||||
|
function updateEditorSize(element) { |
||||
|
var newWidth = 600; |
||||
|
var newHeight = 200; |
||||
|
angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px") |
||||
|
.width(newWidth.toString() + "px"); |
||||
|
vm.editor.resize(); |
||||
|
} |
||||
|
|
||||
|
function loadAlarm() { |
||||
|
alarmService.getAlarmInfo(vm.alarmId).then( |
||||
|
function success(alarm) { |
||||
|
vm.alarm = alarm; |
||||
|
loadAlarmFields(); |
||||
|
}, |
||||
|
function fail() { |
||||
|
vm.alarm = null; |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function loadAlarmFields() { |
||||
|
vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'); |
||||
|
vm.startTime = null; |
||||
|
if (vm.alarm.startTs) { |
||||
|
vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss'); |
||||
|
} |
||||
|
vm.endTime = null; |
||||
|
if (vm.alarm.endTs) { |
||||
|
vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss'); |
||||
|
} |
||||
|
vm.ackTime = null; |
||||
|
if (vm.alarm.ackTs) { |
||||
|
vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss') |
||||
|
} |
||||
|
vm.clearTime = null; |
||||
|
if (vm.alarm.clearTs) { |
||||
|
vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'); |
||||
|
} |
||||
|
|
||||
|
vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name); |
||||
|
|
||||
|
vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status); |
||||
|
|
||||
|
vm.alarmDetails = null; |
||||
|
if (vm.alarm.details) { |
||||
|
vm.alarmDetails = angular.toJson(vm.alarm.details); |
||||
|
vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function acknowledge () { |
||||
|
alarmService.ackAlarm(vm.alarmId).then( |
||||
|
function success() { |
||||
|
vm.alarmUpdated = true; |
||||
|
loadAlarm(); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function clear () { |
||||
|
alarmService.clearAlarm(vm.alarmId).then( |
||||
|
function success() { |
||||
|
vm.alarmUpdated = true; |
||||
|
loadAlarm(); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function close () { |
||||
|
$mdDialog.hide(vm.alarmUpdated ? vm.alarm : null); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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-alarm-details-panel { |
||||
|
margin-left: 15px; |
||||
|
border: 1px solid #C0C0C0; |
||||
|
height: 100%; |
||||
|
#tb-alarm-details { |
||||
|
min-width: 600px; |
||||
|
min-height: 200px; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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 aria-label="{{ 'alarm.alarm-details' | translate }}"> |
||||
|
<md-toolbar> |
||||
|
<div class="md-toolbar-tools"> |
||||
|
<h2 translate>alarm.alarm-details</h2> |
||||
|
<span flex></span> |
||||
|
<md-button class="md-icon-button" ng-click="vm.close()"> |
||||
|
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</md-toolbar> |
||||
|
<md-dialog-content> |
||||
|
<div class="md-dialog-content" layout="column"> |
||||
|
<div layout="row"> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>alarm.created-time</label> |
||||
|
<input ng-model="vm.createdTime" readonly> |
||||
|
</md-input-container> |
||||
|
<md-input-container flex class="md-block"> |
||||
|
<label translate>alarm.originator</label> |
||||
|
<input ng-model="vm.alarm.originatorName" readonly> |
||||
|
</md-input-container> |
||||
|
</div> |
||||
|
<div layout="row" ng-if="vm.startTime || vm.endTime"> |
||||
|
<md-input-container ng-if="vm.startTime" flex class="md-block"> |
||||
|
<label translate>alarm.start-time</label> |
||||
|
<input ng-model="vm.startTime" readonly> |
||||
|
</md-input-container> |
||||
|
<md-input-container ng-if="vm.endTime" flex class="md-block"> |
||||
|
<label translate>alarm.end-time</label> |
||||
|
<input ng-model="vm.endTime" readonly> |
||||
|
</md-input-container> |
||||
|
<span flex ng-if="!vm.startTime || !vm.endTime"></span> |
||||
|
</div> |
||||
|
<div layout="row" ng-if="vm.ackTime || vm.clearTime"> |
||||
|
<md-input-container ng-if="vm.ackTime" flex class="md-block"> |
||||
|
<label translate>alarm.ack-time</label> |
||||
|
<input ng-model="vm.ackTime" readonly> |
||||
|
</md-input-container> |
||||
|
<md-input-container ng-if="vm.clearTime" flex class="md-block"> |
||||
|
<label translate>alarm.clear-time</label> |
||||
|
<input ng-model="vm.clearTime" readonly> |
||||
|
</md-input-container> |
||||
|
<span flex ng-if="!vm.ackTime || !vm.clearTime"></span> |
||||
|
</div> |
||||
|
<div layout="row"> |
||||
|
<md-input-container flex class="md-block"> |
||||
|
<label translate>alarm.type</label> |
||||
|
<input ng-model="vm.alarm.type" readonly> |
||||
|
</md-input-container> |
||||
|
<md-input-container flex class="md-block"> |
||||
|
<label translate>alarm.severity</label> |
||||
|
<input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class" |
||||
|
ng-model="vm.alarmSeverity" readonly> |
||||
|
</md-input-container> |
||||
|
<md-input-container flex class="md-block"> |
||||
|
<label translate>alarm.status</label> |
||||
|
<input ng-model="vm.alarmStatus" readonly> |
||||
|
</md-input-container> |
||||
|
</div> |
||||
|
<div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div> |
||||
|
<div flex class="tb-alarm-details-panel" layout="column"> |
||||
|
<div flex id="tb-alarm-details" readonly |
||||
|
ui-ace="vm.alarmDetailsOptions" |
||||
|
ng-model="vm.alarmDetails"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</md-dialog-content> |
||||
|
<md-dialog-actions layout="row"> |
||||
|
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack || |
||||
|
vm.alarm.status==vm.types.alarmStatus.clearedUnack" |
||||
|
class="md-raised md-primary" |
||||
|
ng-disabled="loading" |
||||
|
ng-click="vm.acknowledge()" |
||||
|
style="margin-right:20px;">{{ 'alarm.acknowledge' | |
||||
|
translate }} |
||||
|
</md-button> |
||||
|
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck || |
||||
|
vm.alarm.status==vm.types.alarmStatus.activeUnack" |
||||
|
class="md-raised md-primary" |
||||
|
ng-disabled="loading" |
||||
|
ng-click="vm.clear()">{{ 'alarm.clear' | |
||||
|
translate }} |
||||
|
</md-button> |
||||
|
<span flex></span> |
||||
|
<md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | |
||||
|
translate }} |
||||
|
</md-button> |
||||
|
</md-dialog-actions> |
||||
|
</md-dialog> |
||||
@ -0,0 +1,39 @@ |
|||||
|
/* |
||||
|
* 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 alarmHeaderTemplate from './alarm-header.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function AlarmHeaderDirective($compile, $templateCache) { |
||||
|
|
||||
|
var linker = function (scope, element) { |
||||
|
|
||||
|
var template = $templateCache.get(alarmHeaderTemplate); |
||||
|
element.html(template); |
||||
|
$compile(element.contents())(scope); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "A", |
||||
|
replace: false, |
||||
|
link: linker, |
||||
|
scope: false |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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 translate class="tb-cell" flex="30">alarm.created-time</div> |
||||
|
<div translate class="tb-cell" flex="15">alarm.originator</div> |
||||
|
<div translate class="tb-cell" flex="20">alarm.type</div> |
||||
|
<div translate class="tb-cell" flex="15">alarm.severity</div> |
||||
|
<div translate class="tb-cell" flex="20">alarm.status</div> |
||||
|
<div translate class="tb-cell" flex="15">alarm.details</div> |
||||
@ -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. |
||||
|
*/ |
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html'; |
||||
|
|
||||
|
import alarmRowTemplate from './alarm-row.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) { |
||||
|
|
||||
|
var linker = function (scope, element, attrs) { |
||||
|
|
||||
|
var template = $templateCache.get(alarmRowTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.alarm = attrs.alarm; |
||||
|
scope.types = types; |
||||
|
|
||||
|
scope.showAlarmDetails = function($event) { |
||||
|
var onShowingCallback = { |
||||
|
onShowing: function(){} |
||||
|
} |
||||
|
$mdDialog.show({ |
||||
|
controller: 'AlarmDetailsDialogController', |
||||
|
controllerAs: 'vm', |
||||
|
templateUrl: alarmDetailsDialogTemplate, |
||||
|
locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback}, |
||||
|
parent: angular.element($document[0].body), |
||||
|
targetEvent: $event, |
||||
|
fullscreen: true, |
||||
|
skipHide: true, |
||||
|
onShowing: function(scope, element) { |
||||
|
onShowingCallback.onShowing(scope, element); |
||||
|
} |
||||
|
}).then(function (alarm) { |
||||
|
if (alarm) { |
||||
|
scope.alarm = alarm; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "A", |
||||
|
replace: false, |
||||
|
link: linker, |
||||
|
scope: false |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,211 @@ |
|||||
|
/* |
||||
|
* 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 './alarm.scss'; |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import alarmTableTemplate from './alarm-table.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) { |
||||
|
|
||||
|
var linker = function (scope, element) { |
||||
|
|
||||
|
var template = $templateCache.get(alarmTableTemplate); |
||||
|
|
||||
|
element.html(template); |
||||
|
|
||||
|
scope.types = types; |
||||
|
|
||||
|
scope.alarmSearchStatus = types.alarmSearchStatus.any; |
||||
|
|
||||
|
var pageSize = 20; |
||||
|
var startTime = 0; |
||||
|
var endTime = 0; |
||||
|
|
||||
|
scope.timewindow = { |
||||
|
history: { |
||||
|
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
scope.topIndex = 0; |
||||
|
|
||||
|
scope.theAlarms = { |
||||
|
getItemAtIndex: function (index) { |
||||
|
if (index > scope.alarms.data.length) { |
||||
|
scope.theAlarms.fetchMoreItems_(index); |
||||
|
return null; |
||||
|
} |
||||
|
var item = scope.alarms.data[index]; |
||||
|
if (item) { |
||||
|
item.indexNumber = index + 1; |
||||
|
} |
||||
|
return item; |
||||
|
}, |
||||
|
|
||||
|
getLength: function () { |
||||
|
if (scope.alarms.hasNext) { |
||||
|
return scope.alarms.data.length + scope.alarms.nextPageLink.limit; |
||||
|
} else { |
||||
|
return scope.alarms.data.length; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
fetchMoreItems_: function () { |
||||
|
if (scope.alarms.hasNext && !scope.alarms.pending) { |
||||
|
if (scope.entityType && scope.entityId && scope.alarmSearchStatus) { |
||||
|
var promise = alarmService.getAlarms(scope.entityType, scope.entityId, |
||||
|
scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false); |
||||
|
if (promise) { |
||||
|
scope.alarms.pending = true; |
||||
|
promise.then( |
||||
|
function success(alarms) { |
||||
|
scope.alarms.data = scope.alarms.data.concat(alarms.data); |
||||
|
scope.alarms.nextPageLink = alarms.nextPageLink; |
||||
|
scope.alarms.hasNext = alarms.hasNext; |
||||
|
if (scope.alarms.hasNext) { |
||||
|
scope.alarms.nextPageLink.limit = pageSize; |
||||
|
} |
||||
|
scope.alarms.pending = false; |
||||
|
}, |
||||
|
function fail() { |
||||
|
scope.alarms.hasNext = false; |
||||
|
scope.alarms.pending = false; |
||||
|
}); |
||||
|
} else { |
||||
|
scope.alarms.hasNext = false; |
||||
|
} |
||||
|
} else { |
||||
|
scope.alarms.hasNext = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
scope.$watch("entityId", function(newVal, prevVal) { |
||||
|
if (newVal && !angular.equals(newVal, prevVal)) { |
||||
|
resetFilter(); |
||||
|
reload(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
|
||||
|
function destroyWatchers() { |
||||
|
if (scope.alarmSearchStatusWatchHandle) { |
||||
|
scope.alarmSearchStatusWatchHandle(); |
||||
|
scope.alarmSearchStatusWatchHandle = null; |
||||
|
} |
||||
|
if (scope.timewindowWatchHandle) { |
||||
|
scope.timewindowWatchHandle(); |
||||
|
scope.timewindowWatchHandle = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function initWatchers() { |
||||
|
scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) { |
||||
|
if (newVal && !angular.equals(newVal, prevVal)) { |
||||
|
reload(); |
||||
|
} |
||||
|
}); |
||||
|
scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) { |
||||
|
if (newVal && !angular.equals(newVal, prevVal)) { |
||||
|
reload(); |
||||
|
} |
||||
|
}, true); |
||||
|
} |
||||
|
|
||||
|
function resetFilter() { |
||||
|
destroyWatchers(); |
||||
|
scope.timewindow = { |
||||
|
history: { |
||||
|
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
||||
|
} |
||||
|
}; |
||||
|
scope.alarmSearchStatus = types.alarmSearchStatus.any; |
||||
|
initWatchers(); |
||||
|
} |
||||
|
|
||||
|
function updateTimeWindowRange () { |
||||
|
if (scope.timewindow.history.timewindowMs) { |
||||
|
var currentTime = (new Date).getTime(); |
||||
|
startTime = currentTime - scope.timewindow.history.timewindowMs; |
||||
|
endTime = currentTime; |
||||
|
} else { |
||||
|
startTime = scope.timewindow.history.fixedTimewindow.startTimeMs; |
||||
|
endTime = scope.timewindow.history.fixedTimewindow.endTimeMs; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function reload () { |
||||
|
scope.topIndex = 0; |
||||
|
scope.selected = []; |
||||
|
updateTimeWindowRange(); |
||||
|
scope.alarms = { |
||||
|
data: [], |
||||
|
nextPageLink: { |
||||
|
limit: pageSize, |
||||
|
startTime: startTime, |
||||
|
endTime: endTime |
||||
|
}, |
||||
|
hasNext: true, |
||||
|
pending: false |
||||
|
}; |
||||
|
scope.theAlarms.getItemAtIndex(pageSize); |
||||
|
} |
||||
|
|
||||
|
scope.noData = function() { |
||||
|
return scope.alarms.data.length == 0 && !scope.alarms.hasNext; |
||||
|
} |
||||
|
|
||||
|
scope.hasData = function() { |
||||
|
return scope.alarms.data.length > 0; |
||||
|
} |
||||
|
|
||||
|
scope.loading = function() { |
||||
|
return $rootScope.loading; |
||||
|
} |
||||
|
|
||||
|
scope.hasScroll = function() { |
||||
|
var repeatContainer = scope.repeatContainer[0]; |
||||
|
if (repeatContainer) { |
||||
|
var scrollElement = repeatContainer.children[0]; |
||||
|
if (scrollElement) { |
||||
|
return scrollElement.scrollHeight > scrollElement.clientHeight; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
reload(); |
||||
|
|
||||
|
initWatchers(); |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
entityType: '=', |
||||
|
entityId: '=' |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
@ -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. |
||||
|
|
||||
|
--> |
||||
|
<md-content flex class="md-padding tb-absolute-fill" layout="column"> |
||||
|
<section layout="row"> |
||||
|
<md-input-container class="md-block" style="width: 200px;"> |
||||
|
<label translate>alarm.alarm-status</label> |
||||
|
<md-select ng-model="alarmSearchStatus" ng-disabled="loading()"> |
||||
|
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus"> |
||||
|
{{ ('alarm.search-status.' + searchStatus) | translate }} |
||||
|
</md-option> |
||||
|
</md-select> |
||||
|
</md-input-container> |
||||
|
<tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> |
||||
|
</section> |
||||
|
<div flex layout="column" class="tb-alarm-container md-whiteframe-z1"> |
||||
|
<md-list flex layout="column" class="tb-alarm-table"> |
||||
|
<md-list class="tb-row tb-header" layout="row" tb-alarm-header> |
||||
|
</md-list> |
||||
|
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" |
||||
|
ng-show="loading()"></md-progress-linear> |
||||
|
<md-divider></md-divider> |
||||
|
<span translate layout-align="center center" |
||||
|
style="margin-top: 25px;" |
||||
|
class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span> |
||||
|
<md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> |
||||
|
<md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> |
||||
|
<md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}"> |
||||
|
</md-list> |
||||
|
<md-divider flex></md-divider> |
||||
|
</md-list-item> |
||||
|
</md-virtual-repeat-container> |
||||
|
</md-list> |
||||
|
</div> |
||||
|
</md-content> |
||||
@ -0,0 +1,78 @@ |
|||||
|
/** |
||||
|
* 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-alarm-container { |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
|
||||
|
md-list.tb-alarm-table { |
||||
|
padding: 0px; |
||||
|
min-width: 700px; |
||||
|
|
||||
|
md-list-item { |
||||
|
padding: 0px; |
||||
|
} |
||||
|
|
||||
|
.tb-row { |
||||
|
height: 48px; |
||||
|
padding: 0px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.tb-row:hover { |
||||
|
background-color: #EEEEEE; |
||||
|
} |
||||
|
|
||||
|
.tb-header:hover { |
||||
|
background: none; |
||||
|
} |
||||
|
|
||||
|
.tb-header { |
||||
|
.tb-cell { |
||||
|
color: rgba(0,0,0,.54); |
||||
|
font-size: 12px; |
||||
|
font-weight: 700; |
||||
|
white-space: nowrap; |
||||
|
background: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-cell { |
||||
|
padding: 0 24px; |
||||
|
margin: auto 0; |
||||
|
color: rgba(0,0,0,.87); |
||||
|
font-size: 13px; |
||||
|
vertical-align: middle; |
||||
|
text-align: left; |
||||
|
overflow: hidden; |
||||
|
.md-button { |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-cell.tb-number { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#tb-alarm-content { |
||||
|
min-width: 400px; |
||||
|
min-height: 50px; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
@ -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. |
||||
|
*/ |
||||
|
|
||||
|
import AlarmDetailsDialogController from './alarm-details-dialog.controller'; |
||||
|
import AlarmHeaderDirective from './alarm-header.directive'; |
||||
|
import AlarmRowDirective from './alarm-row.directive'; |
||||
|
import AlarmTableDirective from './alarm-table.directive'; |
||||
|
|
||||
|
export default angular.module('thingsboard.alarm', []) |
||||
|
.controller('AlarmDetailsDialogController', AlarmDetailsDialogController) |
||||
|
.directive('tbAlarmHeader', AlarmHeaderDirective) |
||||
|
.directive('tbAlarmRow', AlarmRowDirective) |
||||
|
.directive('tbAlarmTable', AlarmTableDirective) |
||||
|
.name; |
||||
@ -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,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); |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue