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