Commit 029317ed by Torkel Ödegaard

tech: alert list react migration progress

parent 5a571f47
...@@ -278,7 +278,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { ...@@ -278,7 +278,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
} }
var response models.AlertStateType = models.AlertStatePending var response models.AlertStateType = models.AlertStatePending
pausedState := "un paused" pausedState := "un-paused"
if cmd.Paused { if cmd.Paused {
response = models.AlertStatePaused response = models.AlertStatePaused
pausedState = "paused" pausedState = "paused"
...@@ -287,7 +287,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { ...@@ -287,7 +287,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
result := map[string]interface{}{ result := map[string]interface{}{
"alertId": alertId, "alertId": alertId,
"state": response, "state": response,
"message": "alert " + pausedState, "message": "Alert " + pausedState,
} }
return Json(200, result) return Json(200, result)
......
...@@ -88,7 +88,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error { ...@@ -88,7 +88,7 @@ func HandleAlertsQuery(query *m.GetAlertsQuery) error {
params = append(params, query.PanelId) params = append(params, query.PanelId)
} }
if len(query.State) > 0 && query.State[0] != "ALL" { if len(query.State) > 0 && query.State[0] != "all" {
sql.WriteString(` AND (`) sql.WriteString(` AND (`)
for i, v := range query.State { for i, v := range query.State {
if i > 0 { if i > 0 {
......
...@@ -2,49 +2,73 @@ import React from 'react'; ...@@ -2,49 +2,73 @@ import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import PageHeader from 'app/core/components/PageHeader/PageHeader'; import PageHeader from 'app/core/components/PageHeader/PageHeader';
import { IRootStore } from 'app/stores/RootStore';
import { IAlertRule } from 'app/stores/AlertListStore';
import appEvents from 'app/core/app_events';
export interface IProps { export interface AlertRuleListProps {
store: any; store: IRootStore;
} }
@inject('store') @inject('store')
@observer @observer
export class AlertRuleList extends React.Component<IProps, any> { export class AlertRuleList extends React.Component<AlertRuleListProps, any> {
stateFilters = [
{ text: 'All', value: 'all' },
{ text: 'OK', value: 'ok' },
{ text: 'Not OK', value: 'not_ok' },
{ text: 'Alerting', value: 'alerting' },
{ text: 'No Data', value: 'no_data' },
{ text: 'Paused', value: 'paused' },
];
constructor(props) { constructor(props) {
super(props); super(props);
this.props.store.nav.load('alerting', 'alert-list'); this.props.store.nav.load('alerting', 'alert-list');
this.props.store.alerting.loadRules(); this.props.store.alertList.loadRules();
} }
onStateFilterChanged = evt => {
this.props.store.alertList.setStateFilter(evt.target.value);
this.props.store.alertList.loadRules();
};
onOpenHowTo = () => {
appEvents.emit('show-modal', {
src: 'public/app/features/alerting/partials/alert_howto.html',
modalClass: 'confirm-modal',
model: {},
});
};
render() { render() {
const { nav, alertList } = this.props.store;
return ( return (
<div> <div>
<PageHeader model={this.props.store.nav} /> <PageHeader model={nav as any} />
<div className="page-container page-body"> <div className="page-container page-body">
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form"> <div className="gf-form">
<label className="gf-form-label">Filter by state</label> <label className="gf-form-label">Filter by state</label>
<div className="gf-form-select-wrapper width-13"> <div className="gf-form-select-wrapper width-13">
<select <select className="gf-form-input" onChange={this.onStateFilterChanged}>
className="gf-form-input" {this.stateFilters.map(AlertStateFilterOption)}
ng-model="ctrl.filters.state" </select>
ng-options="f.value as f.text for f in ctrl.stateFilters"
ng-change="ctrl.filtersChanged()"
/>
</div> </div>
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
<a className="btn btn-secondary" ng-click="ctrl.openHowTo()"> <a className="btn btn-secondary" onClick={this.onOpenHowTo}>
<i className="fa fa-info-circle" /> How to add an alert <i className="fa fa-info-circle" /> How to add an alert
</a> </a>
</div> </div>
<section className="card-section card-list-layout-list"> <section className="card-section card-list-layout-list">
<ol className="card-list">{this.props.store.alerting.rules.map(AlertRuleItem)}</ol> <ol className="card-list">{alertList.rules.map(rule => <AlertRuleItem rule={rule} key={rule.id} />)}</ol>
</section> </section>
</div> </div>
</div> </div>
...@@ -52,43 +76,68 @@ export class AlertRuleList extends React.Component<IProps, any> { ...@@ -52,43 +76,68 @@ export class AlertRuleList extends React.Component<IProps, any> {
} }
} }
function AlertRuleItem(rule) { function AlertStateFilterOption({ text, value }) {
let stateClass = classNames({ return (
fa: true, <option key={value} value={value}>
'fa-play': rule.state === 'paused', {text}
'fa-pause': rule.state !== 'paused', </option>
}); );
}
let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`; export interface AlertRuleItemProps {
rule: IAlertRule;
}
return ( @observer
<li className="card-item-wrapper" key={rule.id}> export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
<div className="card-item card-item--alert"> toggleState = () => {
<div className="card-item-header"> this.props.rule.togglePaused();
<div className="card-item-type"> };
<a className="card-item-cog" title="Pausing an alert rule prevents it from executing">
<i className={stateClass} /> render() {
</a> const { rule } = this.props;
<a className="card-item-cog" href={ruleUrl} title="Edit alert rule">
<i className="icon-gf icon-gf-settings" /> let stateClass = classNames({
</a> fa: true,
</div> 'fa-play': rule.isPaused,
</div> 'fa-pause': !rule.isPaused,
<div className="card-item-body"> });
<div className="card-item-details">
<div className="card-item-name"> let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
<a href={ruleUrl}>{rule.name}</a>
return (
<li className="card-item-wrapper">
<div className="card-item card-item--alert">
<div className="card-item-header">
<div className="card-item-type">
<a
className="card-item-cog"
title="Pausing an alert rule prevents it from executing"
onClick={this.toggleState}
>
<i className={stateClass} />
</a>
<a className="card-item-cog" href={ruleUrl} title="Edit alert rule">
<i className="icon-gf icon-gf-settings" />
</a>
</div> </div>
<div className="card-item-sub-name"> </div>
<span className={`alert-list-item-state ${rule.stateClass}`}> <div className="card-item-body">
<i className={rule.stateIcon} /> {rule.stateText} <div className="card-item-details">
</span> <div className="card-item-name">
<span> for {rule.stateAge}</span> <a href={ruleUrl}>{rule.name}</a>
</div>
<div className="card-item-sub-name">
<span className={`alert-list-item-state ${rule.stateClass}`}>
<i className={rule.stateIcon} /> {rule.stateText}
</span>
<span> for {rule.stateAge}</span>
</div>
{rule.info && <div className="small muted">{rule.info}</div>}
</div> </div>
{rule.info && <div className="small muted">{rule.info}</div>}
</div> </div>
</div> </div>
</div> </li>
</li> );
); }
} }
...@@ -2,40 +2,74 @@ import { types, getEnv, flow } from 'mobx-state-tree'; ...@@ -2,40 +2,74 @@ import { types, getEnv, flow } from 'mobx-state-tree';
import moment from 'moment'; import moment from 'moment';
import alertDef from 'app/features/alerting/alert_def'; import alertDef from 'app/features/alerting/alert_def';
export const AlertRule = types.model('AlertRule', { function setStateFields(rule, state) {
id: types.identifier(types.number), let stateModel = alertDef.getStateDisplayModel(state);
dashboardId: types.number, rule.state = state;
panelId: types.number, rule.stateText = stateModel.text;
name: types.string, rule.stateIcon = stateModel.iconClass;
state: types.string, rule.stateClass = stateModel.stateClass;
stateText: types.string, rule.stateAge = moment(rule.newStateDate)
stateIcon: types.string, .fromNow()
stateClass: types.string, .replace(' ago', '');
stateAge: types.string, }
info: types.optional(types.string, ''),
dashboardUri: types.string, export const AlertRule = types
}); .model('AlertRule', {
id: types.identifier(types.number),
export const AlertingStore = types dashboardId: types.number,
.model('AlertingStore', { panelId: types.number,
name: types.string,
state: types.string,
stateText: types.string,
stateIcon: types.string,
stateClass: types.string,
stateAge: types.string,
info: types.optional(types.string, ''),
dashboardUri: types.string,
})
.views(self => ({
get isPaused() {
return self.state === 'paused';
},
}))
.actions(self => ({
/**
* will toggle alert rule paused state
*/
togglePaused: flow(function* togglePaused() {
let backendSrv = getEnv(self).backendSrv;
var payload = { paused: self.isPaused };
let res = yield backendSrv.post(`/api/alerts/${self.id}/pause`, payload);
setStateFields(self, res.state);
self.info = '';
}),
}));
type IAlertRuleType = typeof AlertRule.Type;
export interface IAlertRule extends IAlertRuleType {}
export const AlertListStore = types
.model('AlertListStore', {
rules: types.array(AlertRule), rules: types.array(AlertRule),
stateFilter: types.optional(types.string, 'all'),
}) })
.actions(self => ({ .actions(self => ({
setStateFilter: function(state) {
self.stateFilter = state;
},
loadRules: flow(function* load() { loadRules: flow(function* load() {
let backendSrv = getEnv(self).backendSrv; let backendSrv = getEnv(self).backendSrv;
let rules = yield backendSrv.get('/api/alerts'); let filters = { state: self.stateFilter };
let rules = yield backendSrv.get('/api/alerts', filters);
self.rules.clear(); self.rules.clear();
for (let rule of rules) { for (let rule of rules) {
let stateModel = alertDef.getStateDisplayModel(rule.state); setStateFields(rule, rule.state);
rule.stateText = stateModel.text;
rule.stateIcon = stateModel.iconClass;
rule.stateClass = stateModel.stateClass;
rule.stateAge = moment(rule.newStateDate)
.fromNow()
.replace(' ago', '');
if (rule.executionError) { if (rule.executionError) {
rule.info = 'Execution Error: ' + rule.executionError; rule.info = 'Execution Error: ' + rule.executionError;
......
...@@ -2,7 +2,7 @@ import { types } from 'mobx-state-tree'; ...@@ -2,7 +2,7 @@ import { types } from 'mobx-state-tree';
import { SearchStore } from './SearchStore'; import { SearchStore } from './SearchStore';
import { ServerStatsStore } from './ServerStatsStore'; import { ServerStatsStore } from './ServerStatsStore';
import { NavStore } from './NavStore'; import { NavStore } from './NavStore';
import { AlertingStore } from './AlertingStore'; import { AlertListStore } from './AlertListStore';
export const RootStore = types.model({ export const RootStore = types.model({
search: types.optional(SearchStore, { search: types.optional(SearchStore, {
...@@ -12,7 +12,7 @@ export const RootStore = types.model({ ...@@ -12,7 +12,7 @@ export const RootStore = types.model({
stats: [], stats: [],
}), }),
nav: types.optional(NavStore, {}), nav: types.optional(NavStore, {}),
alerting: types.optional(AlertingStore, { alertList: types.optional(AlertListStore, {
rules: [], rules: [],
}), }),
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment