Browse Source

Merge pull request #167 from thingsboard/feature/TB-63

TB-63: Add new 'Alarms' tab in entity details.
pull/168/head
Igor Kulikov 9 years ago
committed by GitHub
parent
commit
0726fbb546
  1. 13
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 12
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 1
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  4. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  5. 17
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  6. 134
      ui/src/app/alarm/alarm-details-dialog.controller.js
  7. 27
      ui/src/app/alarm/alarm-details-dialog.scss
  8. 107
      ui/src/app/alarm/alarm-details-dialog.tpl.html
  9. 39
      ui/src/app/alarm/alarm-header.directive.js
  10. 9
      ui/src/app/alarm/alarm-header.tpl.html
  11. 67
      ui/src/app/alarm/alarm-row.directive.js
  12. 17
      ui/src/app/alarm/alarm-row.tpl.html
  13. 211
      ui/src/app/alarm/alarm-table.directive.js
  14. 49
      ui/src/app/alarm/alarm-table.tpl.html
  15. 78
      ui/src/app/alarm/alarm.scss
  16. 27
      ui/src/app/alarm/index.js
  17. 16
      ui/src/app/api/alarm.service.js
  18. 7
      ui/src/app/asset/assets.tpl.html
  19. 2
      ui/src/app/asset/index.js
  20. 26
      ui/src/app/common/types.constant.js
  21. 7
      ui/src/app/customer/customers.tpl.html
  22. 7
      ui/src/app/device/devices.tpl.html
  23. 2
      ui/src/app/device/index.js
  24. 2
      ui/src/app/event/event-content-dialog.controller.js
  25. 2
      ui/src/app/event/event-content-dialog.tpl.html
  26. 4
      ui/src/app/event/event-header.directive.js
  27. 4
      ui/src/app/event/event-row.directive.js
  28. 2
      ui/src/app/event/event-table.tpl.html
  29. 4
      ui/src/app/event/event.scss
  30. 4
      ui/src/app/layout/index.js
  31. 1
      ui/src/app/locale/locale.constant-es.js
  32. 1
      ui/src/app/locale/locale.constant-ko.js
  33. 1
      ui/src/app/locale/locale.constant-ru.js
  34. 1
      ui/src/app/locale/locale.constant-zh.js
  35. 37
      ui/src/app/locale/locale.constant.js
  36. 2
      ui/src/app/plugin/index.js
  37. 8
      ui/src/app/plugin/plugins.tpl.html
  38. 2
      ui/src/app/rule/index.js
  39. 8
      ui/src/app/rule/rules.tpl.html
  40. 7
      ui/src/app/tenant/tenants.tpl.html
  41. 18
      ui/src/scss/main.scss

13
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -57,6 +57,19 @@ public class AlarmController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
@ResponseBody
public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
checkParameter("alarmId", strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmInfoId(alarmId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST)
@ResponseBody

12
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -351,6 +352,17 @@ public abstract class BaseController {
}
}
AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
try {
validateId(alarmId, "Incorrect alarmId " + alarmId);
AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get();
checkAlarm(alarmInfo);
return alarmInfo;
} catch (Exception e) {
throw handleException(e, false);
}
}
protected void checkAlarm(Alarm alarm) throws ThingsboardException {
checkNotNull(alarm);
checkTenantId(alarm.getTenantId());

1
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -55,6 +55,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
public Alarm(Alarm alarm) {
super(alarm.getId());
this.createdTime = alarm.getCreatedTime();
this.tenantId = alarm.getTenantId();
this.type = alarm.getType();
this.originator = alarm.getOriginator();

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -35,6 +35,8 @@ public interface AlarmService {
ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId);
ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
}

17
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -198,6 +198,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
return alarmDao.findAlarmByIdAsync(alarmId.getId());
}
@Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
(AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
AlarmInfo alarmInfo = new AlarmInfo(alarm1);
return Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
originatorName -> {
alarmInfo.setOriginatorName(originatorName);
return alarmInfo;
}
);
});
}
@Override
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);

134
ui/src/app/alarm/alarm-details-dialog.controller.js

@ -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);
}
}

27
ui/src/app/alarm/alarm-details-dialog.scss

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

107
ui/src/app/alarm/alarm-details-dialog.tpl.html

@ -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>

39
ui/src/app/alarm/alarm-header.directive.js

@ -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
};
}

9
ui/src/app/event/event-header-alarm.tpl.html → ui/src/app/alarm/alarm-header.tpl.html

@ -15,6 +15,9 @@
limitations under the License.
-->
<div translate class="tb-cell" flex="30">event.event-time</div>
<div translate class="tb-cell" flex="20">event.server</div>
<div translate class="tb-cell" flex="20">event.alarm</div>
<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>

67
ui/src/app/alarm/alarm-row.directive.js

@ -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
};
}

17
ui/src/app/event/event-row-alarm.tpl.html → ui/src/app/alarm/alarm-row.tpl.html

@ -15,14 +15,19 @@
limitations under the License.
-->
<div class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
<div class="tb-cell" flex="20">{{event.body.server}}</div>
<div class="tb-cell" flex="20">
<md-button ng-if="event.body.body" class="md-icon-button md-primary"
ng-click="showContent($event, event.body.body, 'event.alarm')"
<div class="tb-cell" flex="30">{{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
<div class="tb-cell" flex="15">{{alarm.originatorName}}</div>
<div class="tb-cell" flex="20">{{alarm.type}}</div>
<div class="tb-cell tb-severity" flex="15" ng-class="types.alarmSeverity[alarm.severity].class">
{{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }}
</div>
<div class="tb-cell" flex="20">{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}</div>
<div class="tb-cell" flex="15">
<md-button class="md-icon-button md-primary"
ng-click="showAlarmDetails($event)"
aria-label="{{ 'action.view' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.view' | translate }}
{{ 'alarm.details' | translate }}
</md-tooltip>
<md-icon aria-label="{{ 'action.view' | translate }}"
class="material-icons">

211
ui/src/app/alarm/alarm-table.directive.js

@ -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: '='
}
};
}

49
ui/src/app/alarm/alarm-table.tpl.html

@ -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>

78
ui/src/app/alarm/alarm.scss

@ -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%;
}

27
ui/src/app/alarm/index.js

@ -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;

16
ui/src/app/api/alarm.service.js

@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', [])
function AlarmService($http, $q, $interval, $filter) {
var service = {
getAlarm: getAlarm,
getAlarmInfo: getAlarmInfo,
saveAlarm: saveAlarm,
ackAlarm: ackAlarm,
clearAlarm: clearAlarm,
@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) {
return deferred.promise;
}
function getAlarmInfo(alarmId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/alarm/info/' + alarmId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function saveAlarm(alarm, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/alarm';

7
ui/src/app/asset/assets.tpl.html

@ -48,11 +48,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.asset"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.asset"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/asset/index.js

@ -15,7 +15,6 @@
*/
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardEvent from '../event';
import thingsboardApiUser from '../api/user.service';
import thingsboardApiAsset from '../api/asset.service';
import thingsboardApiCustomer from '../api/customer.service';
@ -29,7 +28,6 @@ import AssetDirective from './asset.directive';
export default angular.module('thingsboard.asset', [
uiRouter,
thingsboardGrid,
thingsboardEvent,
thingsboardApiUser,
thingsboardApiAsset,
thingsboardApiCustomer

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

@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', [])
ack: "ACK",
unack: "UNACK"
},
alarmSeverity: {
"CRITICAL": {
name: "alarm.severity-critical",
class: "tb-critical"
},
"MAJOR": {
name: "alarm.severity-major",
class: "tb-major"
},
"MINOR": {
name: "alarm.severity-minor",
class: "tb-minor"
},
"WARNING": {
name: "alarm.severity-warning",
class: "tb-warning"
},
"INDETERMINATE": {
name: "alarm.severity-indeterminate",
class: "tb-indeterminate"
}
},
aliasFilterType: {
entityList: {
value: 'entityList',
@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', [])
manages: "Manages"
},
eventType: {
alarm: {
value: "ALARM",
name: "event.type-alarm"
},
error: {
value: "ERROR",
name: "event.type-error"

7
ui/src/app/customer/customers.tpl.html

@ -48,11 +48,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.customer"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.customer"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

7
ui/src/app/device/devices.tpl.html

@ -49,11 +49,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.device"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.device"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/device/index.js

@ -15,7 +15,6 @@
*/
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardEvent from '../event';
import thingsboardApiUser from '../api/user.service';
import thingsboardApiDevice from '../api/device.service';
import thingsboardApiCustomer from '../api/customer.service';
@ -30,7 +29,6 @@ import DeviceDirective from './device.directive';
export default angular.module('thingsboard.device', [
uiRouter,
thingsboardGrid,
thingsboardEvent,
thingsboardApiUser,
thingsboardApiDevice,
thingsboardApiCustomer

2
ui/src/app/event/event-content-dialog.controller.js

@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title,
}
newWidth = 8 * maxLineLength + 16;
}
$('#tb-content', element).height(newHeight.toString() + "px")
$('#tb-event-content', element).height(newHeight.toString() + "px")
.width(newWidth.toString() + "px");
vm.editor.resize();
}

2
ui/src/app/event/event-content-dialog.tpl.html

@ -27,7 +27,7 @@
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content">
<div flex id="tb-content" readonly
<div flex id="tb-event-content" readonly
ui-ace="vm.contentOptions"
ng-model="vm.content">
</div>

4
ui/src/app/event/event-header.directive.js

@ -18,7 +18,6 @@
import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
import eventHeaderErrorTemplate from './event-header-error.tpl.html';
import eventHeaderAlarmTemplate from './event-header-alarm.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
case types.eventType.error.value:
template = eventHeaderErrorTemplate;
break;
case types.eventType.alarm.value:
template = eventHeaderAlarmTemplate;
break;
}
return $templateCache.get(template);
}

4
ui/src/app/event/event-row.directive.js

@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
import eventRowStatsTemplate from './event-row-stats.tpl.html';
import eventRowErrorTemplate from './event-row-error.tpl.html';
import eventRowAlarmTemplate from './event-row-alarm.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
case types.eventType.error.value:
template = eventRowErrorTemplate;
break;
case types.eventType.alarm.value:
template = eventRowAlarmTemplate;
break;
}
return $templateCache.get(template);
}

2
ui/src/app/event/event-table.tpl.html

@ -27,7 +27,7 @@
</md-input-container>
<tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
</section>
<md-list flex layout="column" class="md-whiteframe-z1 tb-table">
<md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
<md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
</md-list>
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"

4
ui/src/app/event/event.scss

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
md-list.tb-table {
md-list.tb-event-table {
padding: 0px;
md-list-item {
@ -64,7 +64,7 @@ md-list.tb-table {
}
#tb-content {
#tb-event-content {
min-width: 400px;
min-height: 50px;
width: 100%;

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

@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple
import thingsboardUserMenu from './user-menu.directive';
import thingsboardEntity from '../entity';
import thingsboardEvent from '../event';
import thingsboardAlarm from '../alarm';
import thingsboardTenant from '../tenant';
import thingsboardCustomer from '../customer';
import thingsboardUser from '../user';
@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [
thingsboardHomeLinks,
thingsboardUserMenu,
thingsboardEntity,
thingsboardEvent,
thingsboardAlarm,
thingsboardTenant,
thingsboardCustomer,
thingsboardUser,

1
ui/src/app/locale/locale.constant-es.js

@ -411,7 +411,6 @@
},
"event": {
"event-type": "Tipo de evento",
"type-alarm": "Alarma",
"type-error": "Error",
"type-lc-event": "Ciclo de vida",
"type-stats": "Estadísticas",

1
ui/src/app/locale/locale.constant-ko.js

@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) {
},
"event": {
"event-type": "이벤트 타입",
"type-alarm": "알람",
"type-error": "에러",
"type-lc-event": "주기적 이벤트",
"type-stats": "통계",

1
ui/src/app/locale/locale.constant-ru.js

@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) {
},
"event": {
"event-type": "Тип события",
"type-alarm": "Аварийное оповещение",
"type-error": "Ошибка",
"type-lc-event": "Событие жизненного цикла",
"type-stats": "Статистика",

1
ui/src/app/locale/locale.constant-zh.js

@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) {
},
"event" : {
"event-type": "事件类型",
"type-alarm": "报警",
"type-error": "错误",
"type-lc-event": "生命周期事件",
"type-stats": "类型统计",

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

@ -108,9 +108,43 @@ export default angular.module('thingsboard.locale', [])
},
"alarm": {
"alarm": "Alarm",
"alarms": "Alarms",
"select-alarm": "Select alarm",
"no-alarms-matching": "No alarms matching '{{entity}}' were found.",
"alarm-required": "Alarm is required"
"alarm-required": "Alarm is required",
"alarm-status": "Alarm status",
"search-status": {
"ANY": "Any",
"ACTIVE": "Active",
"CLEARED": "Cleared",
"ACK": "Acknowledged",
"UNACK": "Unacknowledged"
},
"display-status": {
"ACTIVE_UNACK": "Active Unacknowledged",
"ACTIVE_ACK": "Active Acknowledged",
"CLEARED_UNACK": "Cleared Unacknowledged",
"CLEARED_ACK": "Cleared Acknowledged"
},
"no-alarms-prompt": "No alarms found",
"created-time": "Created time",
"type": "Type",
"severity": "Severity",
"originator": "Originator",
"details": "Details",
"status": "Status",
"alarm-details": "Alarm details",
"start-time": "Start time",
"end-time": "End time",
"ack-time": "Acknowledged time",
"clear-time": "Cleared time",
"severity-critical": "Critical",
"severity-major": "Major",
"severity-minor": "Minor",
"severity-warning": "Warning",
"severity-indeterminate": "Indeterminate",
"acknowledge": "Acknowledge",
"clear": "Clear"
},
"alias": {
"add": "Add alias",
@ -647,7 +681,6 @@ export default angular.module('thingsboard.locale', [])
},
"event": {
"event-type": "Event type",
"type-alarm": "Alarm",
"type-error": "Error",
"type-lc-event": "Lifecycle event",
"type-stats": "Statistics",

2
ui/src/app/plugin/index.js

@ -16,7 +16,6 @@
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardJsonForm from '../components/json-form.directive';
import thingsboardEvent from '../event';
import thingsboardApiPlugin from '../api/plugin.service';
import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [
uiRouter,
thingsboardGrid,
thingsboardJsonForm,
thingsboardEvent,
thingsboardApiPlugin,
thingsboardApiComponentDescriptor
])

8
ui/src/app/plugin/plugins.tpl.html

@ -48,12 +48,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.plugin"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.plugin"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.lcEvent.value}}"
disabled-event-types="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.lcEvent.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/rule/index.js

@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardPluginSelect from '../components/plugin-select.directive';
import thingsboardComponent from '../component';
import thingsboardEvent from '../event';
import thingsboardApiRule from '../api/rule.service';
import thingsboardApiPlugin from '../api/plugin.service';
import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [
thingsboardGrid,
thingsboardPluginSelect,
thingsboardComponent,
thingsboardEvent,
thingsboardApiRule,
thingsboardApiPlugin,
thingsboardApiComponentDescriptor

8
ui/src/app/rule/rules.tpl.html

@ -48,12 +48,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.rule"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.rule"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.lcEvent.value}}"
disabled-event-types="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.lcEvent.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">

7
ui/src/app/tenant/tenants.tpl.html

@ -46,11 +46,16 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.tenant"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.tenant"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.types.id.nullUid"
default-event-type="{{vm.types.eventType.alarm.value}}">
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

18
ui/src/scss/main.scss

@ -309,6 +309,24 @@ pre.tb-highlight {
}
}
.tb-severity {
font-weight: bold;
&.tb-critical {
color: red !important;
}
&.tb-major {
color: orange !important;
}
&.tb-minor {
color: #ffca3d !important;
}
&.tb-warning {
color: #abab00 !important;
}
&.tb-indeterminate {
color: green !important;
}
}
/***********************
* Flow

Loading…
Cancel
Save