Merge branch 'master' into bugfix/1345-alert-level-colors
commit
e4d5e912db
|
@ -7,8 +7,12 @@
|
|||
|
||||
### Features
|
||||
### UI Improvements
|
||||
1. [#1378](https://github.com/influxdata/chronograf/pull/1378): Save query time range for dashboards
|
||||
1. [#1365](https://github.com/influxdata/chronograf/pull/1365): Show red indicator on Hosts Page for an offline host
|
||||
1. [#1373](https://github.com/influxdata/chronograf/pull/1373): Re-address dashboard cell stacking contexts
|
||||
1. [#602](https://github.com/influxdata/chronograf/pull/602): Normalize terminology in app
|
||||
1. [#1392](https://github.com/influxdata/chronograf/pull/1392): Overlays are now full screen
|
||||
1. [#1395](https://github.com/influxdata/chronograf/pull/1395): Change default global time range to past 1 hour
|
||||
1. [#1379](https://github.com/influxdata/chronograf/pull/1379): Re-add alert level colors on the alerts page
|
||||
|
||||
## v1.2.0-beta10 [2017-04-28]
|
||||
|
|
|
@ -289,6 +289,12 @@ type GroupBy struct {
|
|||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// DurationRange represents the lower and upper durations of the query config
|
||||
type DurationRange struct {
|
||||
Upper string `json:"upper"`
|
||||
Lower string `json:"lower"`
|
||||
}
|
||||
|
||||
// QueryConfig represents UI query from the data explorer
|
||||
type QueryConfig struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
@ -300,6 +306,7 @@ type QueryConfig struct {
|
|||
GroupBy GroupBy `json:"groupBy"`
|
||||
AreTagsAccepted bool `json:"areTagsAccepted"`
|
||||
RawText *string `json:"rawText"`
|
||||
Range *DurationRange `json:"range"`
|
||||
}
|
||||
|
||||
// KapacitorNode adds arguments and properties to an alert
|
||||
|
|
104
influx/query.go
104
influx/query.go
|
@ -2,6 +2,7 @@ package influx
|
|||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/influxdb/influxql"
|
||||
|
@ -174,6 +175,13 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// If the condition has a time range we report back its duration
|
||||
if dur, ok := hasTimeRange(stmt.Condition); ok {
|
||||
qc.Range = &chronograf.DurationRange{
|
||||
Lower: "now() - " + shortDur(dur),
|
||||
}
|
||||
}
|
||||
|
||||
return qc, nil
|
||||
}
|
||||
|
||||
|
@ -202,31 +210,36 @@ func isNow(exp influxql.Expr) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func isDuration(exp influxql.Expr) bool {
|
||||
func isDuration(exp influxql.Expr) (time.Duration, bool) {
|
||||
switch e := exp.(type) {
|
||||
case *influxql.ParenExpr:
|
||||
return isDuration(e.Expr)
|
||||
case *influxql.DurationLiteral, *influxql.NumberLiteral, *influxql.IntegerLiteral, *influxql.TimeLiteral:
|
||||
return true
|
||||
case *influxql.DurationLiteral:
|
||||
return e.Val, true
|
||||
case *influxql.NumberLiteral, *influxql.IntegerLiteral, *influxql.TimeLiteral:
|
||||
return 0, false
|
||||
}
|
||||
return false
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func isPreviousTime(exp influxql.Expr) bool {
|
||||
func isPreviousTime(exp influxql.Expr) (time.Duration, bool) {
|
||||
if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return isPreviousTime(p.Expr)
|
||||
} else if bin, ok := exp.(*influxql.BinaryExpr); ok {
|
||||
now := isNow(bin.LHS) || isNow(bin.RHS) // either side can be now
|
||||
op := bin.Op == influxql.SUB
|
||||
dur := isDuration(bin.LHS) || isDuration(bin.RHS) // either side can be a isDuration
|
||||
return now && op && dur
|
||||
dur, hasDur := isDuration(bin.LHS)
|
||||
if !hasDur {
|
||||
dur, hasDur = isDuration(bin.RHS)
|
||||
}
|
||||
return dur, now && op && hasDur
|
||||
} else if isNow(exp) { // just comparing to now
|
||||
return true
|
||||
return 0, true
|
||||
}
|
||||
return false
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func isTimeRange(exp influxql.Expr) bool {
|
||||
func isTimeRange(exp influxql.Expr) (time.Duration, bool) {
|
||||
if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return isTimeRange(p.Expr)
|
||||
} else if bin, ok := exp.(*influxql.BinaryExpr); ok {
|
||||
|
@ -236,21 +249,28 @@ func isTimeRange(exp influxql.Expr) bool {
|
|||
case influxql.LT, influxql.LTE, influxql.GT, influxql.GTE:
|
||||
op = true
|
||||
}
|
||||
prev := isPreviousTime(bin.LHS) || isPreviousTime(bin.RHS)
|
||||
return tm && op && prev
|
||||
dur, prev := isPreviousTime(bin.LHS)
|
||||
if !prev {
|
||||
dur, prev = isPreviousTime(bin.RHS)
|
||||
}
|
||||
return dur, tm && op && prev
|
||||
}
|
||||
return false
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func hasTimeRange(exp influxql.Expr) bool {
|
||||
func hasTimeRange(exp influxql.Expr) (time.Duration, bool) {
|
||||
if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return hasTimeRange(p.Expr)
|
||||
} else if isTimeRange(exp) {
|
||||
return true
|
||||
} else if dur, ok := isTimeRange(exp); ok {
|
||||
return dur, true
|
||||
} else if bin, ok := exp.(*influxql.BinaryExpr); ok {
|
||||
return isTimeRange(bin.LHS) || isTimeRange(bin.RHS)
|
||||
dur, ok := isTimeRange(bin.LHS)
|
||||
if !ok {
|
||||
dur, ok = isTimeRange(bin.RHS)
|
||||
}
|
||||
return dur, ok
|
||||
}
|
||||
return false
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func isTagLogic(exp influxql.Expr) ([]tagFilter, bool) {
|
||||
|
@ -258,7 +278,7 @@ func isTagLogic(exp influxql.Expr) ([]tagFilter, bool) {
|
|||
return isTagLogic(p.Expr)
|
||||
}
|
||||
|
||||
if isTimeRange(exp) {
|
||||
if _, ok := isTimeRange(exp); ok {
|
||||
return nil, true
|
||||
} else if tf, ok := isTagFilter(exp); ok {
|
||||
return []tagFilter{tf}, true
|
||||
|
@ -280,7 +300,10 @@ func isTagLogic(exp influxql.Expr) ([]tagFilter, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
tm := isTimeRange(bin.LHS) || isTimeRange(bin.RHS)
|
||||
_, tm := isTimeRange(bin.LHS)
|
||||
if !tm {
|
||||
_, tm = isTimeRange(bin.RHS)
|
||||
}
|
||||
tf := lhsOK || rhsOK
|
||||
if tm && tf {
|
||||
if lhsOK {
|
||||
|
@ -307,35 +330,6 @@ func isTagLogic(exp influxql.Expr) ([]tagFilter, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
func hasTagFilter(exp influxql.Expr) bool {
|
||||
if _, ok := isTagFilter(exp); ok {
|
||||
return true
|
||||
} else if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return hasTagFilter(p.Expr)
|
||||
} else if bin, ok := exp.(*influxql.BinaryExpr); ok {
|
||||
or := bin.Op == influxql.OR
|
||||
and := bin.Op == influxql.AND
|
||||
op := or || and
|
||||
return op && (hasTagFilter(bin.LHS) || hasTagFilter(bin.RHS))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func singleTagFilter(exp influxql.Expr) (tagFilter, bool) {
|
||||
if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return singleTagFilter(p.Expr)
|
||||
} else if tf, ok := isTagFilter(exp); ok {
|
||||
return tf, true
|
||||
} else if bin, ok := exp.(*influxql.BinaryExpr); ok && bin.Op == influxql.OR {
|
||||
lhs, lhsOK := singleTagFilter(bin.LHS)
|
||||
rhs, rhsOK := singleTagFilter(bin.RHS)
|
||||
if lhsOK && rhsOK && lhs.Op == rhs.Op && lhs.Tag == rhs.Tag {
|
||||
return lhs, true
|
||||
}
|
||||
}
|
||||
return tagFilter{}, false
|
||||
}
|
||||
|
||||
func isVarRef(exp influxql.Expr) bool {
|
||||
if p, ok := exp.(*influxql.ParenExpr); ok {
|
||||
return isVarRef(p.Expr)
|
||||
|
@ -411,3 +405,15 @@ var supportedFuncs = map[string]bool{
|
|||
"spread": true,
|
||||
"stddev": true,
|
||||
}
|
||||
|
||||
// shortDur converts duration into the queryConfig duration format
|
||||
func shortDur(d time.Duration) string {
|
||||
s := d.String()
|
||||
if strings.HasSuffix(s, "m0s") {
|
||||
s = s[:len(s)-2]
|
||||
}
|
||||
if strings.HasSuffix(s, "h0m") {
|
||||
s = s[:len(s)-2]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -39,6 +39,198 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test range",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time > now() - 15m`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
Tags: map[string][]string{"host": []string{"myhost"}},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test invalid range",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time > now() - 15`,
|
||||
RawText: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time > now() - 15`,
|
||||
want: chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{},
|
||||
Tags: map[string][]string{},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Tags: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test range with no duration",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time > now()`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
Tags: map[string][]string{"host": []string{"myhost"}},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 0s",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test range with no tags",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where time > now() - 15m`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Tags: map[string][]string{},
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test range with no tags nor duration",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where time`,
|
||||
RawText: `SELECT usage_user from telegraf.autogen.cpu where time`,
|
||||
want: chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{},
|
||||
Tags: map[string][]string{},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Tags: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test with no time range",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time`,
|
||||
RawText: `SELECT usage_user from telegraf.autogen.cpu where "host" != 'myhost' and time`,
|
||||
want: chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{},
|
||||
Tags: map[string][]string{},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Tags: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test with no where clauses",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
Tags: map[string][]string{},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test tags accepted",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where "host" = 'myhost' and time > now() - 15m`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
Tags: map[string][]string{"host": []string{"myhost"}},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
AreTagsAccepted: true,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 15m",
|
||||
Upper: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test mixed tag logic",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where ("host" = 'myhost' or "this" = 'those') and ("howdy" != 'doody') and time > now() - 15m`,
|
||||
RawText: `SELECT usage_user from telegraf.autogen.cpu where ("host" = 'myhost' or "this" = 'those') and ("howdy" != 'doody') and time > now() - 15m`,
|
||||
want: chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{},
|
||||
Tags: map[string][]string{},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Tags: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test tags accepted",
|
||||
influxQL: `SELECT usage_user from telegraf.autogen.cpu where ("host" = 'myhost' OR "host" = 'yourhost') and ("these" = 'those') and time > now() - 15m`,
|
||||
want: chronograf.QueryConfig{
|
||||
Database: "telegraf",
|
||||
Measurement: "cpu",
|
||||
RetentionPolicy: "autogen",
|
||||
Fields: []chronograf.Field{
|
||||
chronograf.Field{
|
||||
Field: "usage_user",
|
||||
Funcs: []string{},
|
||||
},
|
||||
},
|
||||
Tags: map[string][]string{
|
||||
"host": []string{"myhost", "yourhost"},
|
||||
"these": []string{"those"},
|
||||
},
|
||||
GroupBy: chronograf.GroupBy{
|
||||
Time: "",
|
||||
Tags: []string{},
|
||||
},
|
||||
AreTagsAccepted: true,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -49,6 +241,11 @@ func TestConvert(t *testing.T) {
|
|||
}
|
||||
if tt.RawText != "" {
|
||||
tt.want.RawText = &tt.RawText
|
||||
if got.RawText == nil {
|
||||
t.Errorf("Convert() = nil, want %s", tt.RawText)
|
||||
} else if *got.RawText != tt.RawText {
|
||||
t.Errorf("Convert() = %s, want %s", *got.RawText, tt.RawText)
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Convert() = %#v, want %#v", got, tt.want)
|
||||
|
|
|
@ -283,6 +283,9 @@ func Test_newDashboardResponse(t *testing.T) {
|
|||
},
|
||||
Tags: make(map[string][]string, 0),
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -299,7 +302,7 @@ func Test_newDashboardResponse(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
if got := newDashboardResponse(tt.d); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("%q. newDashboardResponse() = \n%+v\n\n, want\n\n%+v", tt.name, got, tt.want)
|
||||
t.Errorf("%q. newDashboardResponse() = \n%#v\n\n, want\n\n%#v", tt.name, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2536,7 +2536,11 @@
|
|||
"time": "10m",
|
||||
"tags": []
|
||||
},
|
||||
"areTagsAccepted": true
|
||||
"areTagsAccepted": true,
|
||||
"range": {
|
||||
"lower": "15m",
|
||||
"upper": "now"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
|
@ -2598,6 +2602,21 @@
|
|||
"funcs"
|
||||
]
|
||||
}
|
||||
},
|
||||
"range": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lower": {
|
||||
"type": "string"
|
||||
},
|
||||
"upper": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"lower",
|
||||
"upper"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import reducer from 'src/data_explorer/reducers/timeRange'
|
||||
|
||||
import {
|
||||
setTimeRange,
|
||||
} from 'src/data_explorer/actions/view'
|
||||
import {setTimeRange} from 'src/data_explorer/actions/view'
|
||||
|
||||
const noopAction = () => {
|
||||
return {type: 'NOOP'}
|
||||
|
@ -12,7 +10,7 @@ describe('DataExplorer.Reducers.TimeRange', () => {
|
|||
it('it sets the default timeRange', () => {
|
||||
const state = reducer(undefined, noopAction())
|
||||
const expected = {
|
||||
lower: 'now() - 15m',
|
||||
lower: 'now() - 1h',
|
||||
upper: null,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash'
|
||||
import timeRanges from 'hson!../../shared/data/timeRanges.hson'
|
||||
|
||||
const {lower, upper} = timeRanges[1]
|
||||
const {lower, upper} = timeRanges[2]
|
||||
|
||||
const initialState = {
|
||||
dashboards: [],
|
||||
|
|
|
@ -97,10 +97,12 @@ export function editRawText(queryId, rawText) {
|
|||
}
|
||||
}
|
||||
|
||||
export function setTimeRange(range) {
|
||||
export function setTimeRange(bounds) {
|
||||
return {
|
||||
type: 'SET_TIME_RANGE',
|
||||
payload: range,
|
||||
payload: {
|
||||
bounds,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ class QueryEditor extends Component {
|
|||
|
||||
if (isTemplating) {
|
||||
switch (e.key) {
|
||||
case 'Tab':
|
||||
case 'ArrowRight':
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
import timeRanges from 'hson!../../shared/data/timeRanges.hson'
|
||||
|
||||
const initialLower = timeRanges[1].lower
|
||||
const initialUpper = timeRanges[1].upper
|
||||
const {lower, upper} = timeRanges[2]
|
||||
|
||||
const initialState = {
|
||||
upper: initialUpper,
|
||||
lower: initialLower,
|
||||
upper,
|
||||
lower,
|
||||
}
|
||||
|
||||
export default function timeRange(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case 'SET_TIME_RANGE': {
|
||||
const {upper, lower} = action.payload
|
||||
const newState = {
|
||||
upper,
|
||||
lower,
|
||||
}
|
||||
const {bounds} = action.payload
|
||||
|
||||
return {...state, ...newState}
|
||||
return {...state, ...bounds}
|
||||
}
|
||||
}
|
||||
return state
|
||||
|
|
|
@ -129,7 +129,7 @@ const HostsTable = React.createClass({
|
|||
onClick={() => this.updateSort('name')}
|
||||
className={this.sortableClasses('name')}
|
||||
>
|
||||
Hostname
|
||||
Host
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('deltaUptime')}
|
||||
|
@ -262,7 +262,7 @@ const SearchBar = React.createClass({
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter by Hostname..."
|
||||
placeholder="Filter by Host..."
|
||||
ref="searchInput"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
|
|
@ -45,12 +45,12 @@ export const HostPage = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState() {
|
||||
const fifteenMinutesIndex = 1
|
||||
const timeRange = timeRanges[2]
|
||||
|
||||
return {
|
||||
layouts: [],
|
||||
hosts: [],
|
||||
timeRange: timeRanges[fifteenMinutesIndex],
|
||||
timeRange,
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ export const KapacitorRule = React.createClass({
|
|||
}
|
||||
|
||||
if (this.thresholdValueEmpty() || this.relativeValueEmpty()) {
|
||||
return 'Please enter a value in the Values section'
|
||||
return 'Please enter a value in the Rule Conditions section'
|
||||
}
|
||||
|
||||
return ''
|
||||
|
|
|
@ -28,11 +28,11 @@ const KapacitorRules = ({
|
|||
</PageContents>
|
||||
)
|
||||
}
|
||||
|
||||
const tableHeader = rules.length === 1 ? '1 Alert Rule' : `${rules.length} Alert Rules`
|
||||
return (
|
||||
<PageContents source={source}>
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">Alert Rules</h2>
|
||||
<h2 className="panel-title">{tableHeader}</h2>
|
||||
<Link
|
||||
to={`/sources/${source.id}/alert-rules/new`}
|
||||
className="btn btn-sm btn-primary"
|
||||
|
@ -55,7 +55,7 @@ const PageContents = ({children, source}) => (
|
|||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Kapacitor Rules</h1>
|
||||
<h1 className="page-header__title">Alert Rules</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<SourceIndicator sourceName={source && source.name} />
|
||||
|
|
|
@ -8,7 +8,7 @@ const KapacitorRulesTable = ({source, rules, onDelete, onChangeRuleStatus}) => {
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Trigger</th>
|
||||
<th>Rule Type</th>
|
||||
<th>Message</th>
|
||||
<th>Alerts</th>
|
||||
<th className="text-center">Enabled</th>
|
||||
|
|
|
@ -23,7 +23,7 @@ export const ValuesSection = React.createClass({
|
|||
|
||||
return (
|
||||
<div className="kapacitor-rule-section">
|
||||
<h3 className="rule-section-heading">Values</h3>
|
||||
<h3 className="rule-section-heading">Rule Conditions</h3>
|
||||
<div className="rule-section-body">
|
||||
<Tabs initialIndex={initialIndex} onSelect={this.handleChooseTrigger}>
|
||||
<TabList isKapacitorTabs="true">
|
||||
|
|
|
@ -65,7 +65,7 @@ const SideNav = React.createClass({
|
|||
Alert History
|
||||
</NavListItem>
|
||||
<NavListItem link={`${sourcePrefix}/alert-rules`}>
|
||||
Kapacitor Rules
|
||||
Alert Rules
|
||||
</NavListItem>
|
||||
</NavBlock>
|
||||
<NavBlock icon="crown2" link={`${sourcePrefix}/admin`}>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
@import 'layout/page';
|
||||
@import 'layout/page-header';
|
||||
@import 'layout/sidebar';
|
||||
@import 'layout/overlay';
|
||||
|
||||
// Components
|
||||
@import 'components/confirm-buttons';
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Manages Overlays
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.page, .sidebar {
|
||||
position: relative;
|
||||
}
|
||||
.page {
|
||||
z-index: 2;
|
||||
}
|
||||
.sidebar {
|
||||
z-index: 1;
|
||||
|
||||
// Ensures that sidebar menus appear above the rest of the app on hover
|
||||
&:hover {z-index: 2;}
|
||||
&:hover + .page {z-index: 1;}
|
||||
}
|
||||
|
||||
// Make Overlay Technology full screen
|
||||
.overlay-technology {
|
||||
left: -($sidebar-width) !important;
|
||||
}
|
|
@ -13,9 +13,7 @@
|
|||
color: $g17-whisper;
|
||||
}
|
||||
.page {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-contents {
|
||||
position: absolute;
|
||||
|
|
|
@ -11,7 +11,6 @@ $overlay-z: 100;
|
|||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: $overlay-z;
|
||||
padding: 0 30px;
|
||||
|
|
Loading…
Reference in New Issue