Pārlūkot izejas kodu

tech: began reworking alerting list to mobx

Torkel Ödegaard 8 gadi atpakaļ
vecāks
revīzija
4c1a67c34e

+ 94 - 0
public/app/containers/AlertRuleList/AlertRuleList.tsx

@@ -0,0 +1,94 @@
+import React from 'react';
+import classNames from 'classnames';
+import { inject, observer } from 'mobx-react';
+import PageHeader from 'app/core/components/PageHeader/PageHeader';
+
+export interface IProps {
+  store: any;
+}
+
+@inject('store')
+@observer
+export class AlertRuleList extends React.Component<IProps, any> {
+  constructor(props) {
+    super(props);
+
+    this.props.store.nav.load('alerting', 'alert-list');
+    this.props.store.alerting.loadRules();
+  }
+
+  render() {
+    return (
+      <div>
+        <PageHeader model={this.props.store.nav} />
+        <div className="page-container page-body">
+          <div className="page-action-bar">
+            <div className="gf-form">
+              <label className="gf-form-label">Filter by state</label>
+
+              <div className="gf-form-select-wrapper width-13">
+                <select
+                  className="gf-form-input"
+                  ng-model="ctrl.filters.state"
+                  ng-options="f.value as f.text for f in ctrl.stateFilters"
+                  ng-change="ctrl.filtersChanged()"
+                />
+              </div>
+            </div>
+
+            <div className="page-action-bar__spacer" />
+
+            <a className="btn btn-secondary" ng-click="ctrl.openHowTo()">
+              <i className="fa fa-info-circle" /> How to add an alert
+            </a>
+          </div>
+
+          <section className="card-section card-list-layout-list">
+            <ol className="card-list">{this.props.store.alerting.rules.map(AlertRuleItem)}</ol>
+          </section>
+        </div>
+      </div>
+    );
+  }
+}
+
+function AlertRuleItem(rule) {
+  let stateClass = classNames({
+    fa: true,
+    'fa-play': rule.state === 'paused',
+    'fa-pause': rule.state !== 'paused',
+  });
+
+  let ruleUrl = `dashboard/${rule.dashboardUri}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
+
+  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">
+              <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-body">
+          <div className="card-item-details">
+            <div className="card-item-name">
+              <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>
+      </div>
+    </li>
+  );
+}

+ 1 - 1
public/app/containers/ServerStats/ServerStats.tsx

@@ -12,7 +12,7 @@ export class ServerStats extends React.Component<IProps, any> {
   constructor(props) {
     super(props);
 
-    this.props.store.nav.load('cfg', 'admin', 'server-stats', 1);
+    this.props.store.nav.load('cfg', 'admin', 'server-stats');
     this.props.store.serverStats.load();
   }
 

+ 0 - 2
public/app/features/alerting/alert_def.ts

@@ -1,5 +1,3 @@
-///<reference path="../../headers/common.d.ts" />
-
 import _ from 'lodash';
 import { QueryPartDef, QueryPart } from 'app/core/components/query_part/query_part';
 

+ 7 - 0
public/app/routes/routes.ts

@@ -1,6 +1,7 @@
 import './dashboard_loaders';
 import './ReactContainer';
 import { ServerStats } from 'app/containers/ServerStats/ServerStats';
+import { AlertRuleList } from 'app/containers/AlertRuleList/AlertRuleList';
 
 /** @ngInject **/
 export function setupAngularRoutes($routeProvider, $locationProvider) {
@@ -225,6 +226,12 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
       controller: 'AlertListCtrl',
       controllerAs: 'ctrl',
     })
+    .when('/alerting/list2', {
+      template: '<react-container />',
+      resolve: {
+        component: () => AlertRuleList,
+      },
+    })
     .when('/alerting/notifications', {
       templateUrl: 'public/app/features/alerting/partials/notifications_list.html',
       controller: 'AlertNotificationsListCtrl',

+ 51 - 0
public/app/stores/AlertingStore.ts

@@ -0,0 +1,51 @@
+import { types, getEnv, flow } from 'mobx-state-tree';
+import moment from 'moment';
+import alertDef from 'app/features/alerting/alert_def';
+
+export const AlertRule = types.model('AlertRule', {
+  id: types.identifier(types.number),
+  dashboardId: types.number,
+  panelId: types.number,
+  name: types.string,
+  state: types.string,
+  stateText: types.string,
+  stateIcon: types.string,
+  stateClass: types.string,
+  stateAge: types.string,
+  info: types.string,
+  dashboardUri: types.string,
+});
+
+export const AlertingStore = types
+  .model('AlertingStore', {
+    rules: types.array(AlertRule),
+  })
+  .actions(self => ({
+    loadRules: flow(function* load() {
+      let backendSrv = getEnv(self).backendSrv;
+
+      let rules = yield backendSrv.get('/api/alerts');
+
+      self.rules.clear();
+
+      for (let rule of rules) {
+        let stateModel = alertDef.getStateDisplayModel(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) {
+          rule.info = 'Execution Error: ' + rule.executionError;
+        }
+
+        if (rule.evalData && rule.evalData.noData) {
+          rule.info = 'Query returned no data';
+        }
+
+        self.rules.push(AlertRule.create(rule));
+      }
+    }),
+  }));

+ 2 - 7
public/app/stores/NavStore.ts

@@ -24,18 +24,13 @@ export const NavStore = types
       let parents = [];
 
       for (let id of args) {
-        // if its a number then it's the index to use for main
-        if (_.isNumber(id)) {
-          main = parents[id];
-          break;
-        }
-
         node = _.find(children, { id: id });
-        main = node;
         children = node.children;
         parents.push(node);
       }
 
+      main = parents[parents.length - 2];
+
       if (main.children) {
         for (let item of main.children) {
           item.active = false;

+ 4 - 0
public/app/stores/RootStore.ts

@@ -2,6 +2,7 @@ import { types } from 'mobx-state-tree';
 import { SearchStore } from './SearchStore';
 import { ServerStatsStore } from './ServerStatsStore';
 import { NavStore } from './NavStore';
+import { AlertingStore } from './AlertingStore';
 
 export const RootStore = types.model({
   search: types.optional(SearchStore, {
@@ -11,6 +12,9 @@ export const RootStore = types.model({
     stats: [],
   }),
   nav: types.optional(NavStore, {}),
+  alerting: types.optional(AlertingStore, {
+    rules: [],
+  }),
 });
 
 type IRootStoreType = typeof RootStore.Type;

+ 11 - 16
public/app/stores/ServerStatsStore.tsx

@@ -14,21 +14,16 @@ export const ServerStatsStore = types
     load: flow(function* load() {
       let backendSrv = getEnv(self).backendSrv;
 
-      try {
-        let res = yield backendSrv.get('/api/admin/stats');
-        self.stats.clear();
-        self.stats.push(ServerStat.create({ name: 'Total dashboards', value: res.dashboards }));
-        self.stats.push(ServerStat.create({ name: 'Total users', value: res.users }));
-        self.stats.push(ServerStat.create({ name: 'Active users (seen last 30 days)', value: res.activeUsers }));
-        self.stats.push(ServerStat.create({ name: 'Total orgs', value: res.orgs }));
-        self.stats.push(ServerStat.create({ name: 'Total playlists', value: res.playlists }));
-        self.stats.push(ServerStat.create({ name: 'Total snapshots', value: res.snapshots }));
-        self.stats.push(ServerStat.create({ name: 'Total dashboard tags', value: res.tags }));
-        self.stats.push(ServerStat.create({ name: 'Total starred dashboards', value: res.stars }));
-        self.stats.push(ServerStat.create({ name: 'Total alerts', value: res.alerts }));
-      } catch (err) {
-        console.log('ServerStats.load error', err);
-        self.error = err.toString();
-      }
+      let res = yield backendSrv.get('/api/admin/stats');
+      self.stats.clear();
+      self.stats.push(ServerStat.create({ name: 'Total dashboards', value: res.dashboards }));
+      self.stats.push(ServerStat.create({ name: 'Total users', value: res.users }));
+      self.stats.push(ServerStat.create({ name: 'Active users (seen last 30 days)', value: res.activeUsers }));
+      self.stats.push(ServerStat.create({ name: 'Total orgs', value: res.orgs }));
+      self.stats.push(ServerStat.create({ name: 'Total playlists', value: res.playlists }));
+      self.stats.push(ServerStat.create({ name: 'Total snapshots', value: res.snapshots }));
+      self.stats.push(ServerStat.create({ name: 'Total dashboard tags', value: res.tags }));
+      self.stats.push(ServerStat.create({ name: 'Total starred dashboards', value: res.stars }));
+      self.stats.push(ServerStat.create({ name: 'Total alerts', value: res.alerts }));
     }),
   }));