Merge remote-tracking branch 'origin/master' into 1481-feature/reset_zoom_on_timerange_change
Conflicts: ui/src/shared/components/LayoutRenderer.jspull/1549/head
commit
d8af162555
|
@ -2,6 +2,7 @@
|
|||
|
||||
### Bug Fixes
|
||||
1. [#1530](https://github.com/influxdata/chronograf/pull/1530): Update query config field ordering to always match input query
|
||||
1. [#1535](https://github.com/influxdata/chronograf/pull/1535): Fix add field functions to existing Kapacitor rules
|
||||
|
||||
### Features
|
||||
|
||||
|
@ -9,6 +10,7 @@
|
|||
1. [#1508](https://github.com/influxdata/chronograf/pull/1508): The enter and escape keys now perform as expected when renaming dashboard headers.
|
||||
1. [#1524](https://github.com/influxdata/chronograf/pull/1524): Rewrite UI copy in Kapacitor Node configuration to be more clear
|
||||
1. [#1549](https://github.com/influxdata/chronograf/pull/1549): Reset graph zoom when a new time range is selected
|
||||
1. [#1544](https://github.com/influxdata/chronograf/pull/1544): Upgrade to new version of Influx Theme, remove excess stylesheets
|
||||
|
||||
## v1.3.1.0 [2017-05-22]
|
||||
|
||||
|
@ -35,6 +37,7 @@ In versions 1.3.1+, installing a new version of Chronograf automatically clears
|
|||
|
||||
### UI Improvements
|
||||
1. [#1451](https://github.com/influxdata/chronograf/pull/1451): Refactor scrollbars to support non-webkit browsers
|
||||
1. [#1453](https://github.com/influxdata/chronograf/pull/1453): Increase the query builder's default height in cell editor mode and in the data explorer
|
||||
1. [#1453](https://github.com/influxdata/chronograf/pull/1453): Give QueryMaker a greater initial height than Visualization
|
||||
1. [#1475](https://github.com/influxdata/chronograf/pull/1475): Add ability to toggle visibility of the Template Control Bar
|
||||
1. [#1464](https://github.com/influxdata/chronograf/pull/1464): Make the [template variables](https://docs.influxdata.com/chronograf/v1.3/guides/dashboard-template-variables/) manager more space efficient
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
* base64-arraybuffer 0.1.2 [MIT](https://github.com/niklasvh/base64-arraybuffer)
|
||||
* base64-arraybuffer 0.1.5 [MIT](https://github.com/niklasvh/base64-arraybuffer)
|
||||
* base64-js 1.2.0 [MIT](http://github.com/beatgammit/base64-js)
|
||||
* base64id 0.1.0 [Unknown](https://github.com/faeldt/base64id)
|
||||
* base64id 0.1.0 [MIT](https://github.com/faeldt/base64id/blob/master/LICENSE)
|
||||
* batch 0.5.3 [MIT](https://github.com/visionmedia/batch)
|
||||
* bcrypt-pbkdf 1.0.0 [BSD-4-Clause]((none))
|
||||
* benchmark 1.0.0 [MIT](https://github.com/bestiejs/benchmark.js)
|
||||
|
|
|
@ -99,7 +99,7 @@ func (c *Client) Create(ctx context.Context, rule chronograf.AlertRule) (*Task,
|
|||
Href: task.Link.Href,
|
||||
HrefOutput: c.HrefOutput(kapaID),
|
||||
TICKScript: script,
|
||||
Rule: rule,
|
||||
Rule: c.Reverse(kapaID, script),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -215,6 +215,22 @@ func (c *Client) All(ctx context.Context) (map[string]chronograf.AlertRule, erro
|
|||
return alerts, nil
|
||||
}
|
||||
|
||||
// Reverse builds a chronograf.AlertRule and its QueryConfig from a tickscript
|
||||
func (c *Client) Reverse(id string, script chronograf.TICKScript) chronograf.AlertRule {
|
||||
rule, err := Reverse(script)
|
||||
if err != nil {
|
||||
return chronograf.AlertRule{
|
||||
ID: id,
|
||||
Name: id,
|
||||
Query: nil,
|
||||
TICKScript: script,
|
||||
}
|
||||
}
|
||||
rule.ID = id
|
||||
rule.TICKScript = script
|
||||
return rule
|
||||
}
|
||||
|
||||
// Get returns a single alert in kapacitor
|
||||
func (c *Client) Get(ctx context.Context, id string) (chronograf.AlertRule, error) {
|
||||
kapa, err := c.kapaClient(c.URL, c.Username, c.Password)
|
||||
|
@ -228,18 +244,7 @@ func (c *Client) Get(ctx context.Context, id string) (chronograf.AlertRule, erro
|
|||
}
|
||||
|
||||
script := chronograf.TICKScript(task.TICKscript)
|
||||
rule, err := Reverse(script)
|
||||
if err != nil {
|
||||
return chronograf.AlertRule{
|
||||
ID: task.ID,
|
||||
Name: task.ID,
|
||||
Query: nil,
|
||||
TICKScript: script,
|
||||
}, nil
|
||||
}
|
||||
rule.ID = task.ID
|
||||
rule.TICKScript = script
|
||||
return rule, nil
|
||||
return c.Reverse(task.ID, script), nil
|
||||
}
|
||||
|
||||
// Update changes the tickscript of a given id.
|
||||
|
@ -282,7 +287,7 @@ func (c *Client) Update(ctx context.Context, href string, rule chronograf.AlertR
|
|||
Href: task.Link.Href,
|
||||
HrefOutput: c.HrefOutput(task.ID),
|
||||
TICKScript: script,
|
||||
Rule: rule,
|
||||
Rule: c.Reverse(task.ID, script),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,15 @@ type MockKapa struct {
|
|||
ResTasks []client.Task
|
||||
Error error
|
||||
|
||||
client.CreateTaskOptions
|
||||
*client.CreateTaskOptions
|
||||
client.Link
|
||||
*client.TaskOptions
|
||||
*client.ListTasksOptions
|
||||
client.UpdateTaskOptions
|
||||
*client.UpdateTaskOptions
|
||||
}
|
||||
|
||||
func (m *MockKapa) CreateTask(opt client.CreateTaskOptions) (client.Task, error) {
|
||||
m.CreateTaskOptions = opt
|
||||
m.CreateTaskOptions = &opt
|
||||
return m.ResTask, m.Error
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,9 @@ func (m *MockKapa) ListTasks(opt *client.ListTasksOptions) ([]client.Task, error
|
|||
|
||||
func (m *MockKapa) UpdateTask(link client.Link, opt client.UpdateTaskOptions) (client.Task, error) {
|
||||
m.Link = link
|
||||
m.UpdateTaskOptions = opt
|
||||
if m.UpdateTaskOptions == nil {
|
||||
m.UpdateTaskOptions = &opt
|
||||
}
|
||||
return m.ResTask, m.Error
|
||||
}
|
||||
|
||||
|
@ -49,6 +51,13 @@ func (m *MockKapa) DeleteTask(link client.Link) error {
|
|||
return m.Error
|
||||
}
|
||||
|
||||
type MockID struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (m *MockID) Generate() (string, error) {
|
||||
return m.ID, nil
|
||||
}
|
||||
func TestClient_AllStatus(t *testing.T) {
|
||||
type fields struct {
|
||||
URL string
|
||||
|
@ -160,9 +169,6 @@ func TestClient_AllStatus(t *testing.T) {
|
|||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.AllStatus() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.CreateTaskOptions, tt.createTaskOptions) {
|
||||
t.Errorf("Client.AllStatus() = createTaskOptions %v, want %v", kapa.CreateTaskOptions, tt.createTaskOptions)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||
t.Errorf("Client.AllStatus() = listTasksOptions %v, want %v", kapa.ListTasksOptions, tt.listTasksOptions)
|
||||
}
|
||||
|
@ -438,9 +444,6 @@ trigger
|
|||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.All() = %#v, want %#v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.CreateTaskOptions, tt.createTaskOptions) {
|
||||
t.Errorf("Client.All() = createTaskOptions %v, want %v", kapa.CreateTaskOptions, tt.createTaskOptions)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||
t.Errorf("Client.All() = listTasksOptions %v, want %v", kapa.ListTasksOptions, tt.listTasksOptions)
|
||||
}
|
||||
|
@ -725,9 +728,6 @@ trigger
|
|||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.Get() =\n%#v\nwant\n%#v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.CreateTaskOptions, tt.createTaskOptions) {
|
||||
t.Errorf("Client.Get() = createTaskOptions %v, want %v", kapa.CreateTaskOptions, tt.createTaskOptions)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||
t.Errorf("Client.Get() = listTasksOptions %v, want %v", kapa.ListTasksOptions, tt.listTasksOptions)
|
||||
}
|
||||
|
@ -743,3 +743,410 @@ trigger
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_updateStatus(t *testing.T) {
|
||||
type fields struct {
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
ID chronograf.ID
|
||||
Ticker chronograf.Ticker
|
||||
kapaClient func(url, username, password string) (KapaClient, error)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
href string
|
||||
status client.TaskStatus
|
||||
}
|
||||
kapa := &MockKapa{}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
resTask client.Task
|
||||
want *Task
|
||||
resError error
|
||||
wantErr bool
|
||||
updateTaskOptions *client.UpdateTaskOptions
|
||||
}{
|
||||
{
|
||||
name: "disable alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
href: "/kapacitor/v1/tasks/howdy",
|
||||
status: client.Disabled,
|
||||
},
|
||||
resTask: client.Task{
|
||||
ID: "howdy",
|
||||
Status: client.Disabled,
|
||||
Link: client.Link{
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
},
|
||||
},
|
||||
updateTaskOptions: &client.UpdateTaskOptions{
|
||||
TICKscript: "",
|
||||
Status: client.Disabled,
|
||||
},
|
||||
want: &Task{
|
||||
ID: "howdy",
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
HrefOutput: "/kapacitor/v1/tasks/howdy/output",
|
||||
Rule: chronograf.AlertRule{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail to enable alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
href: "/kapacitor/v1/tasks/howdy",
|
||||
status: client.Enabled,
|
||||
},
|
||||
updateTaskOptions: &client.UpdateTaskOptions{
|
||||
TICKscript: "",
|
||||
Status: client.Enabled,
|
||||
},
|
||||
resError: fmt.Errorf("error"),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "enable alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
href: "/kapacitor/v1/tasks/howdy",
|
||||
status: client.Enabled,
|
||||
},
|
||||
resTask: client.Task{
|
||||
ID: "howdy",
|
||||
Status: client.Enabled,
|
||||
Link: client.Link{
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
},
|
||||
},
|
||||
updateTaskOptions: &client.UpdateTaskOptions{
|
||||
TICKscript: "",
|
||||
Status: client.Enabled,
|
||||
},
|
||||
want: &Task{
|
||||
ID: "howdy",
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
HrefOutput: "/kapacitor/v1/tasks/howdy/output",
|
||||
Rule: chronograf.AlertRule{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
kapa.ResTask = tt.resTask
|
||||
kapa.Error = tt.resError
|
||||
kapa.UpdateTaskOptions = nil
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
URL: tt.fields.URL,
|
||||
Username: tt.fields.Username,
|
||||
Password: tt.fields.Password,
|
||||
ID: tt.fields.ID,
|
||||
Ticker: tt.fields.Ticker,
|
||||
kapaClient: tt.fields.kapaClient,
|
||||
}
|
||||
got, err := c.updateStatus(tt.args.ctx, tt.args.href, tt.args.status)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Client.updateStatus() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.updateStatus() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.UpdateTaskOptions, tt.updateTaskOptions) {
|
||||
t.Errorf("Client.updateStatus() = %v, want %v", kapa.UpdateTaskOptions, tt.updateTaskOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Update(t *testing.T) {
|
||||
type fields struct {
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
ID chronograf.ID
|
||||
Ticker chronograf.Ticker
|
||||
kapaClient func(url, username, password string) (KapaClient, error)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
href string
|
||||
rule chronograf.AlertRule
|
||||
}
|
||||
kapa := &MockKapa{}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
resTask client.Task
|
||||
want *Task
|
||||
resError error
|
||||
wantErr bool
|
||||
updateTaskOptions *client.UpdateTaskOptions
|
||||
}{
|
||||
{
|
||||
name: "update alert rule error",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
href: "/kapacitor/v1/tasks/howdy",
|
||||
rule: chronograf.AlertRule{
|
||||
ID: "howdy",
|
||||
Query: &chronograf.QueryConfig{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
resError: fmt.Errorf("error"),
|
||||
updateTaskOptions: &client.UpdateTaskOptions{
|
||||
TICKscript: "",
|
||||
Type: client.StreamTask,
|
||||
Status: client.Disabled,
|
||||
DBRPs: []client.DBRP{
|
||||
{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
href: "/kapacitor/v1/tasks/howdy",
|
||||
rule: chronograf.AlertRule{
|
||||
ID: "howdy",
|
||||
Query: &chronograf.QueryConfig{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
resTask: client.Task{
|
||||
ID: "howdy",
|
||||
Status: client.Enabled,
|
||||
Link: client.Link{
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
},
|
||||
},
|
||||
updateTaskOptions: &client.UpdateTaskOptions{
|
||||
TICKscript: "",
|
||||
Type: client.StreamTask,
|
||||
Status: client.Disabled,
|
||||
DBRPs: []client.DBRP{
|
||||
{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Task{
|
||||
ID: "howdy",
|
||||
Href: "/kapacitor/v1/tasks/howdy",
|
||||
HrefOutput: "/kapacitor/v1/tasks/howdy/output",
|
||||
Rule: chronograf.AlertRule{
|
||||
ID: "howdy",
|
||||
Name: "howdy",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
kapa.ResTask = tt.resTask
|
||||
kapa.Error = tt.resError
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
URL: tt.fields.URL,
|
||||
Username: tt.fields.Username,
|
||||
Password: tt.fields.Password,
|
||||
ID: tt.fields.ID,
|
||||
Ticker: tt.fields.Ticker,
|
||||
kapaClient: tt.fields.kapaClient,
|
||||
}
|
||||
got, err := c.Update(tt.args.ctx, tt.args.href, tt.args.rule)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Client.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.Update() =\n%#+v\n, want\n%#+v\n", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.UpdateTaskOptions, tt.updateTaskOptions) {
|
||||
t.Errorf("Client.Update() = %v, want %v", kapa.UpdateTaskOptions, tt.updateTaskOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Create(t *testing.T) {
|
||||
type fields struct {
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
ID chronograf.ID
|
||||
Ticker chronograf.Ticker
|
||||
kapaClient func(url, username, password string) (KapaClient, error)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
rule chronograf.AlertRule
|
||||
}
|
||||
kapa := &MockKapa{}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
resTask client.Task
|
||||
want *Task
|
||||
resError error
|
||||
wantErr bool
|
||||
createTaskOptions *client.CreateTaskOptions
|
||||
}{
|
||||
{
|
||||
name: "create alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
ID: &MockID{
|
||||
ID: "howdy",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
rule: chronograf.AlertRule{
|
||||
ID: "howdy",
|
||||
Query: &chronograf.QueryConfig{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
resTask: client.Task{
|
||||
ID: "chronograf-v1-howdy",
|
||||
Status: client.Enabled,
|
||||
Link: client.Link{
|
||||
Href: "/kapacitor/v1/tasks/chronograf-v1-howdy",
|
||||
},
|
||||
},
|
||||
createTaskOptions: &client.CreateTaskOptions{
|
||||
TICKscript: "",
|
||||
ID: "chronograf-v1-howdy",
|
||||
Type: client.StreamTask,
|
||||
Status: client.Enabled,
|
||||
DBRPs: []client.DBRP{
|
||||
{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &Task{
|
||||
ID: "chronograf-v1-howdy",
|
||||
Href: "/kapacitor/v1/tasks/chronograf-v1-howdy",
|
||||
HrefOutput: "/kapacitor/v1/tasks/chronograf-v1-howdy/output",
|
||||
Rule: chronograf.AlertRule{
|
||||
ID: "chronograf-v1-howdy",
|
||||
Name: "chronograf-v1-howdy",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create alert rule",
|
||||
fields: fields{
|
||||
kapaClient: func(url, username, password string) (KapaClient, error) {
|
||||
return kapa, nil
|
||||
},
|
||||
Ticker: &Alert{},
|
||||
ID: &MockID{
|
||||
ID: "howdy",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
rule: chronograf.AlertRule{
|
||||
ID: "howdy",
|
||||
Query: &chronograf.QueryConfig{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
resError: fmt.Errorf("error"),
|
||||
createTaskOptions: &client.CreateTaskOptions{
|
||||
TICKscript: "",
|
||||
ID: "chronograf-v1-howdy",
|
||||
Type: client.StreamTask,
|
||||
Status: client.Enabled,
|
||||
DBRPs: []client.DBRP{
|
||||
{
|
||||
Database: "db",
|
||||
RetentionPolicy: "rp",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
kapa.ResTask = tt.resTask
|
||||
kapa.Error = tt.resError
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
URL: tt.fields.URL,
|
||||
Username: tt.fields.Username,
|
||||
Password: tt.fields.Password,
|
||||
ID: tt.fields.ID,
|
||||
Ticker: tt.fields.Ticker,
|
||||
kapaClient: tt.fields.kapaClient,
|
||||
}
|
||||
got, err := c.Create(tt.args.ctx, tt.args.rule)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Client.Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Client.Create() =\n%v\n, want\n%v\n", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(kapa.CreateTaskOptions, tt.createTaskOptions) {
|
||||
t.Errorf("Client.Create() = %v, want %v", kapa.CreateTaskOptions, tt.createTaskOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -398,6 +398,26 @@ func newAlertResponse(rule chronograf.AlertRule, tickScript chronograf.TICKScrip
|
|||
return res
|
||||
}
|
||||
|
||||
// ValidRuleRequest checks if the requested rule change is valid
|
||||
func ValidRuleRequest(rule chronograf.AlertRule) error {
|
||||
if rule.Query == nil {
|
||||
return fmt.Errorf("invalid alert rule: no query defined")
|
||||
}
|
||||
var hasFuncs bool
|
||||
for _, f := range rule.Query.Fields {
|
||||
if len(f.Funcs) > 0 {
|
||||
hasFuncs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// All kapacitor rules with functions must have a window that is applied
|
||||
// every amount of time
|
||||
if rule.Every == "" && hasFuncs {
|
||||
return fmt.Errorf(`invalid alert rule: functions require an "every" window`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// KapacitorRulesPut proxies PATCH to kapacitor
|
||||
func (h *Service) KapacitorRulesPut(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("kid", r)
|
||||
|
@ -451,8 +471,7 @@ func (h *Service) KapacitorRulesPut(w http.ResponseWriter, r *http.Request) {
|
|||
Error(w, http.StatusInternalServerError, err.Error(), h.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
res := newAlertResponse(req, task.TICKScript, task.Href, task.HrefOutput, "enabled", srv.SrcID, srv.ID)
|
||||
res := newAlertResponse(task.Rule, task.TICKScript, task.Href, task.HrefOutput, "enabled", srv.SrcID, srv.ID)
|
||||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
func TestValidRuleRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rule chronograf.AlertRule
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "No every with functions",
|
||||
rule: chronograf.AlertRule{
|
||||
Query: &chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Field: "oldmanpeabody",
|
||||
Funcs: []string{"max"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "With every",
|
||||
rule: chronograf.AlertRule{
|
||||
Every: "10s",
|
||||
Query: &chronograf.QueryConfig{
|
||||
Fields: []chronograf.Field{
|
||||
{
|
||||
Field: "oldmanpeabody",
|
||||
Funcs: []string{"max"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No query config",
|
||||
rule: chronograf.AlertRule{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := ValidRuleRequest(tt.rule); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidRuleRequest() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import {ALERT_NODES_ACCESSORS} from 'src/kapacitor/constants'
|
|||
|
||||
import {
|
||||
chooseTrigger,
|
||||
addEvery,
|
||||
removeEvery,
|
||||
updateRuleValues,
|
||||
updateDetails,
|
||||
updateMessage,
|
||||
|
@ -38,6 +40,23 @@ describe('Kapacitor.Reducers.rules', () => {
|
|||
expect(newState[ruleID].values).to.equal(defaultRuleConfigs.threshold)
|
||||
})
|
||||
|
||||
it('can update the every', () => {
|
||||
const ruleID = 1
|
||||
const initialState = {
|
||||
[ruleID]: {
|
||||
id: ruleID,
|
||||
queryID: 988,
|
||||
every: null,
|
||||
},
|
||||
}
|
||||
|
||||
let newState = reducer(initialState, addEvery(ruleID, '30s'))
|
||||
expect(newState[ruleID].every).to.equal('30s')
|
||||
|
||||
newState = reducer(newState, removeEvery(ruleID))
|
||||
expect(newState[ruleID].every).to.equal(null)
|
||||
})
|
||||
|
||||
it('can update the values', () => {
|
||||
const ruleID = 1
|
||||
const initialState = {
|
||||
|
@ -50,11 +69,17 @@ describe('Kapacitor.Reducers.rules', () => {
|
|||
}
|
||||
|
||||
const newDeadmanValues = {duration: '5m'}
|
||||
const newState = reducer(initialState, updateRuleValues(ruleID, 'deadman', newDeadmanValues))
|
||||
const newState = reducer(
|
||||
initialState,
|
||||
updateRuleValues(ruleID, 'deadman', newDeadmanValues)
|
||||
)
|
||||
expect(newState[ruleID].values).to.equal(newDeadmanValues)
|
||||
|
||||
const newRelativeValues = {func: 'max', change: 'change'}
|
||||
const finalState = reducer(newState, updateRuleValues(ruleID, 'relative', newRelativeValues))
|
||||
const finalState = reducer(
|
||||
newState,
|
||||
updateRuleValues(ruleID, 'relative', newRelativeValues)
|
||||
)
|
||||
expect(finalState[ruleID].trigger).to.equal('relative')
|
||||
expect(finalState[ruleID].values).to.equal(newRelativeValues)
|
||||
})
|
||||
|
@ -110,7 +135,10 @@ describe('Kapacitor.Reducers.rules', () => {
|
|||
.services('a b c')
|
||||
`
|
||||
|
||||
let newState = reducer(initialState, updateAlertNodes(ruleID, 'alerta', tickScript))
|
||||
let newState = reducer(
|
||||
initialState,
|
||||
updateAlertNodes(ruleID, 'alerta', tickScript)
|
||||
)
|
||||
const expectedStr = `alerta().resource('Hostname or service').event('Something went wrong').environment('Development').group('Dev. Servers').services('a b c')`
|
||||
let actualStr = ALERT_NODES_ACCESSORS.alerta(newState[ruleID])
|
||||
|
||||
|
@ -184,7 +212,10 @@ describe('Kapacitor.Reducers.rules', () => {
|
|||
},
|
||||
}
|
||||
|
||||
const newState = reducer(initialState, updateRuleStatusSuccess(ruleID, status))
|
||||
const newState = reducer(
|
||||
initialState,
|
||||
updateRuleStatusSuccess(ruleID, status)
|
||||
)
|
||||
expect(newState[ruleID].status).to.equal(status)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -96,7 +96,7 @@ const AdminTabs = ({
|
|||
<TabList customClass="col-md-2 admin-tabs">
|
||||
{tabs.map((t, i) => <Tab key={tabs[i].type}>{tabs[i].type}</Tab>)}
|
||||
</TabList>
|
||||
<TabPanels customClass="col-md-10">
|
||||
<TabPanels customClass="col-md-10 admin-tabs--content">
|
||||
{tabs.map((t, i) => (
|
||||
<TabPanel key={tabs[i].type}>{t.component}</TabPanel>
|
||||
))}
|
||||
|
|
|
@ -48,17 +48,17 @@ class ChangePassRow extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {user} = this.props
|
||||
const {user, buttonSize} = this.props
|
||||
|
||||
if (this.state.showForm) {
|
||||
return (
|
||||
<div className="admin-change-pw">
|
||||
<div className="admin-table--change-pw">
|
||||
<input
|
||||
className="form-control"
|
||||
className="form-control input-xs"
|
||||
name="password"
|
||||
type="password"
|
||||
value={user.password || ''}
|
||||
placeholder="Password"
|
||||
placeholder="New password"
|
||||
onChange={this.handleEdit(user)}
|
||||
onKeyPress={this.handleKeyPress(user)}
|
||||
autoFocus={true}
|
||||
|
@ -67,28 +67,29 @@ class ChangePassRow extends Component {
|
|||
onConfirm={this.handleSubmit}
|
||||
item={user}
|
||||
onCancel={this.handleCancel}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="btn btn-xs btn-info admin-table--hidden"
|
||||
onClick={this.showForm}
|
||||
>
|
||||
Change Password
|
||||
</button>
|
||||
<div className="admin-table--change-pw">
|
||||
<a href="#" onClick={this.showForm}>
|
||||
Change
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {shape, func} = PropTypes
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
ChangePassRow.propTypes = {
|
||||
user: shape().isRequired,
|
||||
onApply: func.isRequired,
|
||||
onEdit: func.isRequired,
|
||||
buttonSize: string,
|
||||
}
|
||||
|
||||
export default OnClickOutside(ChangePassRow)
|
||||
|
|
|
@ -25,7 +25,7 @@ const DatabaseManager = ({
|
|||
onDeleteRetentionPolicy,
|
||||
}) => {
|
||||
return (
|
||||
<div className="panel panel-info">
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">
|
||||
{databases.length === 1
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import onClickOutside from 'react-onclickoutside'
|
||||
|
||||
import {formatRPDuration} from 'utils/formatting'
|
||||
import YesNoButtons from 'src/shared/components/YesNoButtons'
|
||||
import onClickOutside from 'react-onclickoutside'
|
||||
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class DatabaseRow extends Component {
|
||||
constructor(props) {
|
||||
|
@ -46,51 +48,55 @@ class DatabaseRow extends Component {
|
|||
<tr>
|
||||
<td>
|
||||
{isNew
|
||||
? <div className="admin-table--edit-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
defaultValue={name}
|
||||
placeholder="Name this RP"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.name = r)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
: <div className="admin-table--edit-cell">
|
||||
{name}
|
||||
</div>}
|
||||
? <input
|
||||
className="form-control input-xs"
|
||||
type="text"
|
||||
defaultValue={name}
|
||||
placeholder="Name this RP"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.name = r)}
|
||||
autoFocus={true}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
: name}
|
||||
</td>
|
||||
<td>
|
||||
<div className="admin-table--edit-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="text"
|
||||
defaultValue={formattedDuration}
|
||||
placeholder="How long should Data last"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.duration = r)}
|
||||
autoFocus={!isNew}
|
||||
/>
|
||||
</div>
|
||||
<td style={{width: `${DATABASE_TABLE.colDuration}px`}}>
|
||||
<input
|
||||
className="form-control input-xs"
|
||||
name="name"
|
||||
type="text"
|
||||
defaultValue={formattedDuration}
|
||||
placeholder="How long should Data last"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.duration = r)}
|
||||
autoFocus={!isNew}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
</td>
|
||||
<td style={isRFDisplayed ? {} : {display: 'none'}}>
|
||||
<div className="admin-table--edit-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="number"
|
||||
min="1"
|
||||
defaultValue={replication || 1}
|
||||
placeholder="# of Nodes"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.replication = r)}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
{isRFDisplayed
|
||||
? <td style={{width: `${DATABASE_TABLE.colReplication}px`}}>
|
||||
<input
|
||||
className="form-control input-xs"
|
||||
name="name"
|
||||
type="number"
|
||||
min="1"
|
||||
defaultValue={replication || 1}
|
||||
placeholder="# of Nodes"
|
||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||
ref={r => (this.replication = r)}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
</td>
|
||||
: null}
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${DATABASE_TABLE.colDelete}px`}}
|
||||
>
|
||||
<YesNoButtons
|
||||
buttonSize="btn-xs"
|
||||
onConfirm={isNew ? this.handleCreate : this.handleUpdate}
|
||||
onCancel={
|
||||
isNew
|
||||
|
@ -106,24 +112,37 @@ class DatabaseRow extends Component {
|
|||
return (
|
||||
<tr>
|
||||
<td>
|
||||
{name}
|
||||
{' '}
|
||||
{`${name} `}
|
||||
{isDefault
|
||||
? <span className="default-source-label">default</span>
|
||||
: null}
|
||||
</td>
|
||||
<td onClick={this.handleStartEdit}>{formattedDuration}</td>
|
||||
<td
|
||||
onClick={this.handleStartEdit}
|
||||
style={{width: `${DATABASE_TABLE.colDuration}px`}}
|
||||
>
|
||||
{formattedDuration}
|
||||
</td>
|
||||
{isRFDisplayed
|
||||
? <td onClick={this.handleStartEdit}>{replication}</td>
|
||||
? <td
|
||||
onClick={this.handleStartEdit}
|
||||
style={{width: `${DATABASE_TABLE.colReplication}px`}}
|
||||
>
|
||||
{replication}
|
||||
</td>
|
||||
: null}
|
||||
<td className="text-right">
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${DATABASE_TABLE.colDelete}px`}}
|
||||
>
|
||||
{isDeleting
|
||||
? <YesNoButtons
|
||||
onConfirm={() => onDelete(database, retentionPolicy)}
|
||||
onCancel={this.handleEndDelete}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
: <button
|
||||
className="btn btn-xs btn-danger admin-table--hidden"
|
||||
className="btn btn-danger btn-xs table--show-on-row-hover"
|
||||
style={isDeletable ? {} : {visibility: 'hidden'}}
|
||||
onClick={this.handleStartDelete}
|
||||
>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import DatabaseRow from 'src/admin/components/DatabaseRow'
|
||||
import DatabaseTableHeader from 'src/admin/components/DatabaseTableHeader'
|
||||
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const {func, shape, bool} = PropTypes
|
||||
|
||||
|
@ -26,7 +28,11 @@ const DatabaseTable = ({
|
|||
onDeleteRetentionPolicy,
|
||||
}) => {
|
||||
return (
|
||||
<div className="db-manager">
|
||||
<div
|
||||
className={classnames('db-manager', {
|
||||
'db-manager--edit': database.isEditing,
|
||||
})}
|
||||
>
|
||||
<DatabaseTableHeader
|
||||
database={database}
|
||||
notify={notify}
|
||||
|
@ -43,13 +49,21 @@ const DatabaseTable = ({
|
|||
isAddRPDisabled={!!database.retentionPolicies.some(rp => rp.isNew)}
|
||||
/>
|
||||
<div className="db-manager-table">
|
||||
<table className="table v-center admin-table">
|
||||
<table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Retention Policy</th>
|
||||
<th>Duration</th>
|
||||
{isRFDisplayed ? <th>Replication Factor</th> : null}
|
||||
<th />
|
||||
<th>
|
||||
Retention Policy
|
||||
</th>
|
||||
<th style={{width: `${DATABASE_TABLE.colDuration}px`}}>
|
||||
Duration
|
||||
</th>
|
||||
{isRFDisplayed
|
||||
? <th style={{width: `${DATABASE_TABLE.colReplication}px`}}>
|
||||
Replication Factor
|
||||
</th>
|
||||
: null}
|
||||
<th style={{width: `${DATABASE_TABLE.colDelete}px`}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -52,12 +52,6 @@ const Header = ({
|
|||
onAddRetentionPolicy,
|
||||
onDatabaseDeleteConfirm,
|
||||
}) => {
|
||||
const confirmStyle = {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}
|
||||
|
||||
const buttons = (
|
||||
<div className="text-right db-manager-header--actions">
|
||||
<button
|
||||
|
@ -87,23 +81,24 @@ const Header = ({
|
|||
}
|
||||
|
||||
const deleteConfirmation = (
|
||||
<div style={confirmStyle}>
|
||||
<div className="admin-table--delete-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="text"
|
||||
value={database.deleteCode || ''}
|
||||
placeholder={`DELETE ${database.name}`}
|
||||
onChange={e => onDatabaseDeleteConfirm(database, e)}
|
||||
onKeyDown={e => onDatabaseDeleteConfirm(database, e)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="admin-table--delete-db">
|
||||
<input
|
||||
className="form-control input-xs"
|
||||
name="name"
|
||||
type="text"
|
||||
value={database.deleteCode || ''}
|
||||
placeholder={`DELETE ${database.name}`}
|
||||
onChange={e => onDatabaseDeleteConfirm(database, e)}
|
||||
onKeyDown={e => onDatabaseDeleteConfirm(database, e)}
|
||||
autoFocus={true}
|
||||
autoComplete={false}
|
||||
spellCheck={false}
|
||||
/>
|
||||
<ConfirmButtons
|
||||
item={database}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -119,14 +114,16 @@ const Header = ({
|
|||
const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
|
||||
<div className="db-manager-header db-manager-header--edit">
|
||||
<input
|
||||
className="form-control"
|
||||
className="form-control input-sm"
|
||||
name="name"
|
||||
type="text"
|
||||
value={database.name}
|
||||
placeholder="Name this database"
|
||||
placeholder="Name this Database"
|
||||
onChange={e => onEdit(database, {name: e.target.value})}
|
||||
onKeyDown={e => onKeyDown(e, database)}
|
||||
autoFocus={true}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
<ConfirmButtons item={database} onConfirm={onConfirm} onCancel={onCancel} />
|
||||
</div>
|
||||
|
|
|
@ -31,7 +31,7 @@ class FilterBar extends Component {
|
|||
<div className="users__search-widget input-group admin__search-widget">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
className="form-control input-sm"
|
||||
placeholder={`Filter ${placeholderText}...`}
|
||||
value={this.state.filterText}
|
||||
onChange={this.handleText}
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import QueryRow from 'src/admin/components/QueryRow'
|
||||
import {QUERIES_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const QueriesTable = ({queries, onKillQuery}) => (
|
||||
<div>
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel panel-default">
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table">
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Database</th>
|
||||
<th style={{width: `${QUERIES_TABLE.colDatabase}px`}}>
|
||||
Database
|
||||
</th>
|
||||
<th>Query</th>
|
||||
<th>Running</th>
|
||||
<th />
|
||||
<th style={{width: `${QUERIES_TABLE.colRunning}px`}}>
|
||||
Running
|
||||
</th>
|
||||
<th style={{width: `${QUERIES_TABLE.colKillQuery}px`}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
|
||||
import ConfirmButtons from 'src/shared/components/ConfirmButtons'
|
||||
import {QUERIES_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class QueryRow extends Component {
|
||||
constructor(props) {
|
||||
|
@ -32,17 +33,31 @@ class QueryRow extends Component {
|
|||
|
||||
return (
|
||||
<tr>
|
||||
<td>{database}</td>
|
||||
<td
|
||||
style={{width: `${QUERIES_TABLE.colDatabase}px`}}
|
||||
className="monotype"
|
||||
>
|
||||
{database}
|
||||
</td>
|
||||
<td><code>{query}</code></td>
|
||||
<td>{duration}</td>
|
||||
<td className="admin-table--kill-button text-right">
|
||||
<td
|
||||
style={{width: `${QUERIES_TABLE.colRunning}px`}}
|
||||
className="monotype"
|
||||
>
|
||||
{duration}
|
||||
</td>
|
||||
<td
|
||||
style={{width: `${QUERIES_TABLE.colKillQuery}px`}}
|
||||
className="text-right"
|
||||
>
|
||||
{this.state.confirmingKill
|
||||
? <ConfirmButtons
|
||||
onConfirm={this.handleFinishHim}
|
||||
onCancel={this.handleShowMercy}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
: <button
|
||||
className="btn btn-xs btn-danger admin-table--hidden"
|
||||
className="btn btn-xs btn-danger table--show-on-row-hover"
|
||||
onClick={this.handleInitiateKill}
|
||||
>
|
||||
Kill
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class RoleEditingRow extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -25,19 +27,19 @@ class RoleEditingRow extends Component {
|
|||
render() {
|
||||
const {role} = this.props
|
||||
return (
|
||||
<td>
|
||||
<div className="admin-table--edit-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="text"
|
||||
value={role.name || ''}
|
||||
placeholder="role name"
|
||||
onChange={this.handleEdit(role)}
|
||||
onKeyPress={this.handleKeyPress(role)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
<td style={{width: `${ROLES_TABLE.colName}px`}}>
|
||||
<input
|
||||
className="form-control input-xs"
|
||||
name="name"
|
||||
type="text"
|
||||
value={role.name || ''}
|
||||
placeholder="Role name"
|
||||
onChange={this.handleEdit(role)}
|
||||
onKeyPress={this.handleKeyPress(role)}
|
||||
autoFocus={true}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
</td>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import RoleEditingRow from 'src/admin/components/RoleEditingRow'
|
||||
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
||||
import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const RoleRow = ({
|
||||
role: {name, permissions, users},
|
||||
|
@ -40,10 +42,18 @@ const RoleRow = ({
|
|||
onSave={onSave}
|
||||
isNew={isNew}
|
||||
/>
|
||||
<td />
|
||||
<td />
|
||||
<td className="text-right" style={{width: '85px'}}>
|
||||
<ConfirmButtons item={role} onConfirm={onSave} onCancel={onCancel} />
|
||||
<td className="admin-table--left-offset">--</td>
|
||||
<td className="admin-table--left-offset">--</td>
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${ROLES_TABLE.colDelete}px`}}
|
||||
>
|
||||
<ConfirmButtons
|
||||
item={role}
|
||||
onConfirm={onSave}
|
||||
onCancel={onCancel}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
@ -51,7 +61,7 @@ const RoleRow = ({
|
|||
|
||||
return (
|
||||
<tr>
|
||||
<td>{name}</td>
|
||||
<td style={{width: `${ROLES_TABLE.colName}px`}}>{name}</td>
|
||||
<td>
|
||||
{allPermissions && allPermissions.length
|
||||
? <MultiSelectDropdown
|
||||
|
@ -59,6 +69,14 @@ const RoleRow = ({
|
|||
selectedItems={perms}
|
||||
label={perms.length ? '' : 'Select Permissions'}
|
||||
onApply={handleUpdatePermissions}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(
|
||||
`dropdown-${ROLES_TABLE.colPermissions}`,
|
||||
{
|
||||
'admin-table--multi-select-empty': !permissions.length,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
: null}
|
||||
</td>
|
||||
|
@ -69,10 +87,22 @@ const RoleRow = ({
|
|||
selectedItems={users === undefined ? [] : users.map(u => u.name)}
|
||||
label={users && users.length ? '' : 'Select Users'}
|
||||
onApply={handleUpdateUsers}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(
|
||||
`dropdown-${ROLES_TABLE.colUsers}`,
|
||||
{
|
||||
'admin-table--multi-select-empty': !users.length,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
: null}
|
||||
</td>
|
||||
<DeleteConfirmTableCell onDelete={onDelete} item={role} />
|
||||
<DeleteConfirmTableCell
|
||||
onDelete={onDelete}
|
||||
item={role}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ const RolesTable = ({
|
|||
onUpdateRoleUsers,
|
||||
onUpdateRolePermissions,
|
||||
}) => (
|
||||
<div className="panel panel-info">
|
||||
<div className="panel panel-default">
|
||||
<FilterBar
|
||||
type="roles"
|
||||
onFilter={onFilter}
|
||||
|
@ -25,12 +25,12 @@ const RolesTable = ({
|
|||
onClickCreate={onClickCreate}
|
||||
/>
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table">
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Permissions</th>
|
||||
<th>Users</th>
|
||||
<th className="admin-table--left-offset">Permissions</th>
|
||||
<th className="admin-table--left-offset">Users</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class UserEditName extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {user} = this.props
|
||||
return (
|
||||
<td style={{width: `${USERS_TABLE.colUsername}px`}}>
|
||||
<input
|
||||
className="form-control input-xs"
|
||||
name="name"
|
||||
type="text"
|
||||
value={user.name || ''}
|
||||
placeholder="Username"
|
||||
onChange={this.handleEdit(user)}
|
||||
onKeyPress={this.handleKeyPress(user)}
|
||||
autoFocus={true}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
</td>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape} = PropTypes
|
||||
|
||||
UserEditName.propTypes = {
|
||||
user: shape().isRequired,
|
||||
onEdit: func.isRequired,
|
||||
onSave: func.isRequired,
|
||||
}
|
||||
|
||||
export default UserEditName
|
|
@ -1,66 +0,0 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
class UserEditingRow extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {user, isNew} = this.props
|
||||
return (
|
||||
<td>
|
||||
<div className="admin-table--edit-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="text"
|
||||
value={user.name || ''}
|
||||
placeholder="Username"
|
||||
onChange={this.handleEdit(user)}
|
||||
onKeyPress={this.handleKeyPress(user)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
{isNew
|
||||
? <input
|
||||
className="form-control"
|
||||
name="password"
|
||||
type="password"
|
||||
value={user.password || ''}
|
||||
placeholder="Password"
|
||||
onChange={this.handleEdit(user)}
|
||||
onKeyPress={this.handleKeyPress(user)}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape} = PropTypes
|
||||
|
||||
UserEditingRow.propTypes = {
|
||||
user: shape().isRequired,
|
||||
isNew: bool,
|
||||
onEdit: func.isRequired,
|
||||
onSave: func.isRequired,
|
||||
}
|
||||
|
||||
export default UserEditingRow
|
|
@ -0,0 +1,58 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
class UserNewPassword extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleKeyPress = ::this.handleKeyPress
|
||||
this.handleEdit = ::this.handleEdit
|
||||
}
|
||||
|
||||
handleKeyPress(user) {
|
||||
return e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.props.onSave(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEdit(user) {
|
||||
return e => {
|
||||
this.props.onEdit(user, {[e.target.name]: e.target.value})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {user, isNew} = this.props
|
||||
return (
|
||||
<td style={{width: `${USERS_TABLE.colPassword}px`}}>
|
||||
{isNew
|
||||
? <input
|
||||
className="form-control input-xs"
|
||||
name="password"
|
||||
type="password"
|
||||
value={user.password || ''}
|
||||
placeholder="Password"
|
||||
onChange={this.handleEdit(user)}
|
||||
onKeyPress={this.handleKeyPress(user)}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
: '--'}
|
||||
</td>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape} = PropTypes
|
||||
|
||||
UserNewPassword.propTypes = {
|
||||
user: shape().isRequired,
|
||||
isNew: bool,
|
||||
onEdit: func.isRequired,
|
||||
onSave: func.isRequired,
|
||||
}
|
||||
|
||||
export default UserNewPassword
|
|
@ -1,12 +1,15 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import UserEditingRow from 'src/admin/components/UserEditingRow'
|
||||
import UserEditName from 'src/admin/components/UserEditName'
|
||||
import UserNewPassword from 'src/admin/components/UserNewPassword'
|
||||
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
||||
import ChangePassRow from 'src/admin/components/ChangePassRow'
|
||||
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const UserRow = ({
|
||||
user: {name, roles, permissions, password},
|
||||
|
@ -42,16 +45,25 @@ const UserRow = ({
|
|||
if (isEditing) {
|
||||
return (
|
||||
<tr className="admin-table--edit-row">
|
||||
<UserEditingRow
|
||||
<UserEditName user={user} onEdit={onEdit} onSave={onSave} />
|
||||
<UserNewPassword
|
||||
user={user}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
isNew={isNew}
|
||||
/>
|
||||
{hasRoles ? <td /> : null}
|
||||
<td />
|
||||
<td className="text-right" style={{width: '85px'}}>
|
||||
<ConfirmButtons item={user} onConfirm={onSave} onCancel={onCancel} />
|
||||
{hasRoles ? <td className="admin-table--left-offset">--</td> : null}
|
||||
<td className="admin-table--left-offset">--</td>
|
||||
<td
|
||||
className="text-right"
|
||||
style={{width: `${USERS_TABLE.colDelete}px`}}
|
||||
>
|
||||
<ConfirmButtons
|
||||
item={user}
|
||||
onConfirm={onSave}
|
||||
onCancel={onCancel}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
@ -59,7 +71,15 @@ const UserRow = ({
|
|||
|
||||
return (
|
||||
<tr>
|
||||
<td>{name}</td>
|
||||
<td style={{width: `${USERS_TABLE.colUsername}px`}}>{name}</td>
|
||||
<td style={{width: `${USERS_TABLE.colPassword}px`}}>
|
||||
<ChangePassRow
|
||||
onEdit={onEdit}
|
||||
onApply={handleUpdatePassword}
|
||||
user={user}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</td>
|
||||
{hasRoles
|
||||
? <td>
|
||||
<MultiSelectDropdown
|
||||
|
@ -71,6 +91,14 @@ const UserRow = ({
|
|||
}
|
||||
label={roles && roles.length ? '' : 'Select Roles'}
|
||||
onApply={handleUpdateRoles}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(
|
||||
`dropdown-${USERS_TABLE.colRoles}`,
|
||||
{
|
||||
'admin-table--multi-select-empty': !roles.length,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
: null}
|
||||
|
@ -83,17 +111,22 @@ const UserRow = ({
|
|||
permissions && permissions.length ? '' : 'Select Permissions'
|
||||
}
|
||||
onApply={handleUpdatePermissions}
|
||||
buttonSize="btn-xs"
|
||||
buttonColor="btn-primary"
|
||||
customClass={classnames(
|
||||
`dropdown-${USERS_TABLE.colPermissions}`,
|
||||
{
|
||||
'admin-table--multi-select-empty': !permissions.length,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
: null}
|
||||
</td>
|
||||
<td className="text-right" style={{width: '300px'}}>
|
||||
<ChangePassRow
|
||||
onEdit={onEdit}
|
||||
onApply={handleUpdatePassword}
|
||||
user={user}
|
||||
/>
|
||||
</td>
|
||||
<DeleteConfirmTableCell onDelete={onDelete} item={user} />
|
||||
<DeleteConfirmTableCell
|
||||
onDelete={onDelete}
|
||||
item={user}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ const UsersTable = ({
|
|||
onUpdateRoles,
|
||||
onUpdatePassword,
|
||||
}) => (
|
||||
<div className="panel panel-info">
|
||||
<div className="panel panel-default">
|
||||
<FilterBar
|
||||
type="users"
|
||||
onFilter={onFilter}
|
||||
|
@ -28,13 +28,13 @@ const UsersTable = ({
|
|||
onClickCreate={onClickCreate}
|
||||
/>
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table">
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
{hasRoles && <th>Roles</th>}
|
||||
<th>Permissions</th>
|
||||
<th />
|
||||
<th>Password</th>
|
||||
{hasRoles && <th className="admin-table--left-offset">Roles</th>}
|
||||
<th className="admin-table--left-offset">Permissions</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
export const USERS_TABLE = {
|
||||
colUsername: 240,
|
||||
colPassword: 186,
|
||||
colRoles: 190,
|
||||
colPermissions: 190,
|
||||
colDelete: 70,
|
||||
}
|
||||
export const ROLES_TABLE = {
|
||||
colName: 280,
|
||||
colUsers: 200,
|
||||
colPermissions: 200,
|
||||
colDelete: 70,
|
||||
}
|
||||
export const QUERIES_TABLE = {
|
||||
colDatabase: 160,
|
||||
colRunning: 80,
|
||||
colKillQuery: 70,
|
||||
}
|
||||
export const ADMIN_TABLE = {
|
||||
colDelete: 70,
|
||||
}
|
||||
export const DATABASE_TABLE = {
|
||||
colRetentionPolicy: 140,
|
||||
colDuration: 80,
|
||||
colReplication: 122,
|
||||
colDelete: 160,
|
||||
}
|
|
@ -89,7 +89,7 @@ const AlertsTable = React.createClass({
|
|||
</div>
|
||||
<div className="panel-body">
|
||||
{this.props.alerts.length
|
||||
? <table className="table v-center">
|
||||
? <table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
|
@ -149,9 +149,9 @@ const AlertsTable = React.createClass({
|
|||
</tbody>
|
||||
</table>
|
||||
: <div className="generic-empty-state">
|
||||
<h4 className="no-user-select">
|
||||
<h5 className="no-user-select">
|
||||
Alerts appear here when you have Rules
|
||||
</h4>
|
||||
</h5>
|
||||
<br />
|
||||
<Link
|
||||
to={`/sources/${id}/alert-rules/new`}
|
||||
|
@ -201,7 +201,7 @@ const SearchBar = React.createClass({
|
|||
value={this.state.searchTerm}
|
||||
/>
|
||||
<div className="input-group-addon">
|
||||
<span className="icon search" aria-hidden="true" />
|
||||
<span className="icon search" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -35,10 +35,10 @@ const DashboardHeader = ({
|
|||
type="button"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<span className="button-text">{buttonText}</span>
|
||||
<span>{buttonText}</span>
|
||||
<span className="caret" />
|
||||
</button>
|
||||
<ul className="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<ul className="dropdown-menu">
|
||||
{children}
|
||||
</ul>
|
||||
</div>}
|
||||
|
@ -55,7 +55,7 @@ const DashboardHeader = ({
|
|||
: null}
|
||||
{dashboard
|
||||
? <button
|
||||
className="btn btn-info btn-sm"
|
||||
className="btn btn-default btn-sm"
|
||||
onClick={onEditDashboard}
|
||||
>
|
||||
<span className="icon pencil" />
|
||||
|
@ -64,7 +64,7 @@ const DashboardHeader = ({
|
|||
: null}
|
||||
{dashboard
|
||||
? <div
|
||||
className={classnames('btn btn-info btn-sm', {
|
||||
className={classnames('btn btn-default btn-sm', {
|
||||
active: showTemplateControlBar,
|
||||
})}
|
||||
onClick={onToggleTempVarControls}
|
||||
|
@ -82,7 +82,7 @@ const DashboardHeader = ({
|
|||
selected={timeRange}
|
||||
/>
|
||||
<div
|
||||
className="btn btn-info btn-sm"
|
||||
className="btn btn-default btn-sm btn-square"
|
||||
onClick={handleClickPresentationButton}
|
||||
>
|
||||
<span className="icon expand-a" style={{margin: 0}} />
|
||||
|
|
|
@ -36,15 +36,20 @@ class DashboardEditHeader extends Component {
|
|||
return (
|
||||
<div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
<form className="page-header__left" onSubmit={this.handleFormSubmit}>
|
||||
<form
|
||||
className="page-header__left"
|
||||
style={{flex: '1 0 0'}}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
>
|
||||
<input
|
||||
className="page-header--editing"
|
||||
name="name"
|
||||
autoFocus={true}
|
||||
value={name}
|
||||
placeholder="Name this Dashboard"
|
||||
onChange={e => this.handleChange(e.target.value)}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
autoFocus={true}
|
||||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</form>
|
||||
|
|
|
@ -12,11 +12,11 @@ const OverlayControls = props => {
|
|||
<h3 className="overlay--graph-name">Cell Editor</h3>
|
||||
<div className="overlay-controls--right">
|
||||
<p>Visualization Type:</p>
|
||||
<ul className="toggle toggle-sm">
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
{graphTypes.map(graphType => (
|
||||
<li
|
||||
key={graphType.type}
|
||||
className={classnames('toggle-btn', {
|
||||
className={classnames({
|
||||
active: graphType.type === selectedGraphType,
|
||||
})}
|
||||
onClick={() => onSelectGraphType(graphType.type)}
|
||||
|
|
|
@ -28,6 +28,7 @@ const TemplateControlBar = ({
|
|||
<Dropdown
|
||||
items={items}
|
||||
buttonSize="btn-xs"
|
||||
menuClass="dropdown-astronaut"
|
||||
useAutoComplete={true}
|
||||
selected={selectedText || '(No values)'}
|
||||
onChoose={item =>
|
||||
|
|
|
@ -84,7 +84,7 @@ const RowButtons = ({
|
|||
<div className="tvm-actions">
|
||||
<DeleteConfirmButtons onDelete={() => onDelete(id)} />
|
||||
<button
|
||||
className="btn btn-sm btn-info btn-edit"
|
||||
className="btn btn-sm btn-info btn-edit btn-square"
|
||||
type="button"
|
||||
onClick={e => {
|
||||
// prevent subsequent 'onSubmit' that is caused by an unknown source,
|
||||
|
|
|
@ -304,10 +304,7 @@ class DashboardPage extends Component {
|
|||
{dashboards
|
||||
? dashboards.map((d, i) => (
|
||||
<li className="dropdown-item" key={i}>
|
||||
<Link
|
||||
to={`/sources/${sourceID}/dashboards/${d.id}`}
|
||||
className="role-option"
|
||||
>
|
||||
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
|
||||
{d.name}
|
||||
</Link>
|
||||
</li>
|
||||
|
|
|
@ -89,7 +89,7 @@ const DashboardsPage = React.createClass({
|
|||
</div>
|
||||
<div className="panel-body">
|
||||
{dashboards && dashboards.length
|
||||
? <table className="table v-center admin-table">
|
||||
? <table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
@ -109,6 +109,7 @@ const DashboardsPage = React.createClass({
|
|||
<DeleteConfirmTableCell
|
||||
onDelete={this.handleDeleteDashboard}
|
||||
item={dashboard}
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
||||
import GroupByTimeDropdown from 'src/data_explorer/components/GroupByTimeDropdown'
|
||||
import GroupByTimeDropdown
|
||||
from 'src/data_explorer/components/GroupByTimeDropdown'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {showFieldKeys} from 'shared/apis/metaQuery'
|
||||
|
@ -77,7 +78,7 @@ const FieldList = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {query} = this.props
|
||||
const {query, isKapacitorRule} = this.props
|
||||
const hasAggregates = query.fields.some(f => f.funcs && f.funcs.length)
|
||||
const hasGroupByTime = query.groupBy.time
|
||||
|
||||
|
@ -90,6 +91,7 @@ const FieldList = React.createClass({
|
|||
isOpen={!hasGroupByTime}
|
||||
selected={query.groupBy.time}
|
||||
onChooseGroupByTime={this.handleGroupByTime}
|
||||
isInRuleBuilder={isKapacitorRule}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
|
|
|
@ -56,7 +56,9 @@ const FieldListItem = React.createClass({
|
|||
if (isKapacitorRule) {
|
||||
return (
|
||||
<div
|
||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
||||
className={classnames('query-builder--list-item', {
|
||||
active: isSelected,
|
||||
})}
|
||||
key={fieldFunc}
|
||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||
>
|
||||
|
@ -66,14 +68,16 @@ const FieldListItem = React.createClass({
|
|||
</span>
|
||||
{isSelected
|
||||
? <Dropdown
|
||||
className="dropdown-110"
|
||||
menuClass="dropdown-malachite"
|
||||
buttonSize="btn-xs"
|
||||
items={items}
|
||||
onChoose={this.handleApplyFunctions}
|
||||
selected={
|
||||
fieldFunc.funcs.length ? fieldFunc.funcs[0] : 'Function'
|
||||
}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -81,7 +85,9 @@ const FieldListItem = React.createClass({
|
|||
return (
|
||||
<div key={fieldFunc}>
|
||||
<div
|
||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
||||
className={classnames('query-builder--list-item', {
|
||||
active: isSelected,
|
||||
})}
|
||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||
>
|
||||
<span>
|
||||
|
@ -89,13 +95,17 @@ const FieldListItem = React.createClass({
|
|||
{fieldText}
|
||||
</span>
|
||||
{isSelected
|
||||
? <div className={classnames('btn btn-xs btn-info', {'function-selector--toggled': isOpen})} onClick={this.toggleFunctionsMenu}>
|
||||
? <div
|
||||
className={classnames('btn btn-xs btn-default', {
|
||||
'function-selector--toggled': isOpen,
|
||||
})}
|
||||
onClick={this.toggleFunctionsMenu}
|
||||
>
|
||||
Functions
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
: null}
|
||||
</div>
|
||||
{(isSelected && isOpen)
|
||||
{isSelected && isOpen
|
||||
? <FunctionSelector
|
||||
onApply={this.handleApplyFunctions}
|
||||
selectedItems={fieldFunc.funcs || []}
|
||||
|
|
|
@ -1,49 +1,33 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import groupByTimeOptions from 'hson!../data/groupByTimes.hson'
|
||||
|
||||
const {bool, string, func} = PropTypes
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
const {bool, func, string} = PropTypes
|
||||
|
||||
const GroupByTimeDropdown = React.createClass({
|
||||
propTypes: {
|
||||
isOpen: bool,
|
||||
selected: string,
|
||||
onChooseGroupByTime: func.isRequired,
|
||||
isInRuleBuilder: bool,
|
||||
},
|
||||
|
||||
render() {
|
||||
const {isOpen, selected, onChooseGroupByTime} = this.props
|
||||
const {selected, onChooseGroupByTime, isInRuleBuilder} = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('dropdown group-by-time-dropdown', {
|
||||
open: isOpen,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className="btn btn-sm btn-info dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<span>Group by {selected || 'time'}</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
{groupByTimeOptions.map(groupBy => {
|
||||
return (
|
||||
<li
|
||||
className="dropdown-item"
|
||||
key={groupBy.menuOption}
|
||||
onClick={() => onChooseGroupByTime(groupBy)}
|
||||
>
|
||||
<a href="#">
|
||||
{groupBy.menuOption}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<Dropdown
|
||||
className="dropdown-130"
|
||||
menuClass={isInRuleBuilder ? 'dropdown-malachite' : null}
|
||||
buttonColor={isInRuleBuilder ? 'btn-default' : 'btn-info'}
|
||||
items={groupByTimeOptions.map(groupBy => ({
|
||||
...groupBy,
|
||||
text: groupBy.menuOption,
|
||||
}))}
|
||||
onChoose={onChooseGroupByTime}
|
||||
selected={selected ? `Group by ${selected}` : 'Group by Time'}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -96,6 +96,8 @@ const MeasurementList = React.createClass({
|
|||
value={this.state.filterText}
|
||||
onChange={this.handleFilterText}
|
||||
onKeyUp={this.handleEscape}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
<span className="icon search" />
|
||||
</div>
|
||||
|
|
|
@ -254,7 +254,8 @@ class QueryEditor extends Component {
|
|||
items={QUERY_TEMPLATES}
|
||||
selected={'Query Templates'}
|
||||
onChoose={this.handleChooseTemplate}
|
||||
className="query-editor--templates"
|
||||
className="dropdown-140 query-editor--templates"
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
|
@ -270,7 +271,8 @@ class QueryEditor extends Component {
|
|||
items={QUERY_TEMPLATES}
|
||||
selected={'Query Templates'}
|
||||
onChoose={this.handleChooseTemplate}
|
||||
className="query-editor--templates"
|
||||
className="dropdown-140 query-editor--templates"
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
|
@ -300,7 +302,8 @@ class QueryEditor extends Component {
|
|||
items={QUERY_TEMPLATES}
|
||||
selected={'Query Templates'}
|
||||
onChoose={this.handleChooseTemplate}
|
||||
className="query-editor--templates"
|
||||
className="dropdown-140 query-editor--templates"
|
||||
buttonSize="btn-xs"
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
|
|
|
@ -66,6 +66,8 @@ const TagListItem = React.createClass({
|
|||
value={this.state.filterText}
|
||||
onChange={this.handleFilterText}
|
||||
onKeyUp={this.handleEscape}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
<span className="icon search" />
|
||||
</div>
|
||||
|
@ -107,7 +109,7 @@ const TagListItem = React.createClass({
|
|||
{tagItemLabel}
|
||||
</span>
|
||||
<div
|
||||
className={classnames('btn btn-info btn-xs group-by-tag', {
|
||||
className={classnames('btn btn-default btn-xs group-by-tag', {
|
||||
active: this.props.isUsingGroupBy,
|
||||
})}
|
||||
onClick={this.handleGroupBy}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
const VisHeader = ({views, view, onToggleView, name}) => (
|
||||
<div className="graph-heading">
|
||||
{views.length
|
||||
? <ul className="toggle toggle-sm">
|
||||
? <ul className="nav nav-tablist nav-tablist-sm">
|
||||
{views.map(v => (
|
||||
<li
|
||||
key={v}
|
||||
onClick={() => onToggleView(v)}
|
||||
className={classnames('toggle-btn ', {active: view === v})}
|
||||
className={classnames({active: view === v})}
|
||||
>
|
||||
{v}
|
||||
{_.upperFirst(v)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -108,8 +108,6 @@ const HostsTable = React.createClass({
|
|||
hostsTitle = 'Loading Hosts...'
|
||||
} else if (hostsError.length) {
|
||||
hostsTitle = 'There was a problem loading hosts'
|
||||
} else if (hosts.length === 0) {
|
||||
hostsTitle = 'No hosts found'
|
||||
} else if (hostCount === 1) {
|
||||
hostsTitle = `${hostCount} Host`
|
||||
} else {
|
||||
|
@ -123,45 +121,52 @@ const HostsTable = React.createClass({
|
|||
<SearchBar onSearch={this.updateSearchTerm} />
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<table className="table v-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
onClick={() => this.updateSort('name')}
|
||||
className={this.sortableClasses('name')}
|
||||
>
|
||||
Host
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('deltaUptime')}
|
||||
className={this.sortableClasses('deltaUptime')}
|
||||
style={{width: '74px'}}
|
||||
>
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('cpu')}
|
||||
className={this.sortableClasses('cpu')}
|
||||
style={{width: '70px'}}
|
||||
>
|
||||
CPU
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('load')}
|
||||
className={this.sortableClasses('load')}
|
||||
style={{width: '68px'}}
|
||||
>
|
||||
Load
|
||||
</th>
|
||||
<th>Apps</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{sortedHosts.map(h => {
|
||||
return <HostRow key={h.name} host={h} source={source} />
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{hostCount > 0 && !hostsError.length
|
||||
? <table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
onClick={() => this.updateSort('name')}
|
||||
className={this.sortableClasses('name')}
|
||||
>
|
||||
Host
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('deltaUptime')}
|
||||
className={this.sortableClasses('deltaUptime')}
|
||||
style={{width: '74px'}}
|
||||
>
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('cpu')}
|
||||
className={this.sortableClasses('cpu')}
|
||||
style={{width: '70px'}}
|
||||
>
|
||||
CPU
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('load')}
|
||||
className={this.sortableClasses('load')}
|
||||
style={{width: '68px'}}
|
||||
>
|
||||
Load
|
||||
</th>
|
||||
<th>Apps</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{sortedHosts.map(h => {
|
||||
return <HostRow key={h.name} host={h} source={source} />
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
: <div className="generic-empty-state">
|
||||
<h4 style={{margin: '90px 0'}}>
|
||||
No Hosts found
|
||||
</h4>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -190,21 +190,20 @@ export const HostPage = React.createClass({
|
|||
>
|
||||
{Object.keys(hosts).map((host, i) => {
|
||||
return (
|
||||
<li key={i}>
|
||||
<Link
|
||||
to={`/sources/${id}/hosts/${host + appParam}`}
|
||||
className="role-option"
|
||||
>
|
||||
<li className="dropdown-item" key={i}>
|
||||
<Link to={`/sources/${id}/hosts/${host + appParam}`}>
|
||||
{host}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</DashboardHeader>
|
||||
<FancyScrollbar className={classnames({
|
||||
'page-contents': true,
|
||||
'presentation-mode': inPresentationMode,
|
||||
})}>
|
||||
<FancyScrollbar
|
||||
className={classnames({
|
||||
'page-contents': true,
|
||||
'presentation-mode': inPresentationMode,
|
||||
})}
|
||||
>
|
||||
<div className="container-fluid full-width dashboard">
|
||||
{layouts.length > 0 ? this.renderLayouts(layouts) : ''}
|
||||
</div>
|
||||
|
|
|
@ -71,6 +71,21 @@ export function chooseTrigger(ruleID, trigger) {
|
|||
}
|
||||
}
|
||||
|
||||
export const addEvery = (ruleID, frequency) => ({
|
||||
type: 'ADD_EVERY',
|
||||
payload: {
|
||||
ruleID,
|
||||
frequency,
|
||||
},
|
||||
})
|
||||
|
||||
export const removeEvery = ruleID => ({
|
||||
type: 'REMOVE_EVERY',
|
||||
payload: {
|
||||
ruleID,
|
||||
},
|
||||
})
|
||||
|
||||
export function updateRuleValues(ruleID, trigger, values) {
|
||||
return {
|
||||
type: 'UPDATE_RULE_VALUES',
|
||||
|
|
|
@ -6,6 +6,8 @@ import DatabaseList from '../../data_explorer/components/DatabaseList'
|
|||
import MeasurementList from '../../data_explorer/components/MeasurementList'
|
||||
import FieldList from '../../data_explorer/components/FieldList'
|
||||
|
||||
import {defaultEveryFrequency} from 'src/kapacitor/constants'
|
||||
|
||||
export const DataSection = React.createClass({
|
||||
propTypes: {
|
||||
source: PropTypes.shape({
|
||||
|
@ -27,6 +29,8 @@ export const DataSection = React.createClass({
|
|||
groupByTime: PropTypes.func.isRequired,
|
||||
toggleTagAcceptance: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
onAddEvery: PropTypes.func.isRequired,
|
||||
onRemoveEvery: PropTypes.func.isRequired,
|
||||
timeRange: PropTypes.shape({}).isRequired,
|
||||
},
|
||||
|
||||
|
@ -53,6 +57,11 @@ export const DataSection = React.createClass({
|
|||
|
||||
handleToggleField(field) {
|
||||
this.props.actions.toggleField(this.props.query.id, field, true)
|
||||
// Every is only added when a function has been added to a field.
|
||||
// Here, the field is selected without a function.
|
||||
this.props.onRemoveEvery()
|
||||
// Because there are no functions there is no group by time.
|
||||
this.props.actions.groupByTime(this.props.query.id, null)
|
||||
},
|
||||
|
||||
handleGroupByTime(time) {
|
||||
|
@ -61,6 +70,7 @@ export const DataSection = React.createClass({
|
|||
|
||||
handleApplyFuncsToField(fieldFunc) {
|
||||
this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc)
|
||||
this.props.onAddEvery(defaultEveryFrequency)
|
||||
},
|
||||
|
||||
handleChooseTag(tag) {
|
||||
|
@ -80,13 +90,13 @@ export const DataSection = React.createClass({
|
|||
const statement = query.rawText || buildInfluxQLQuery({lower}, query)
|
||||
|
||||
return (
|
||||
<div className="kapacitor-rule-section kapacitor-metric-selector">
|
||||
<h3 className="rule-section-heading">Select a Time Series</h3>
|
||||
<div className="rule-section-body">
|
||||
<pre>
|
||||
<div className="rule-section">
|
||||
<h3 className="rule-section--heading">Select a Time Series</h3>
|
||||
<div className="rule-section--body">
|
||||
<pre className="rule-section--border-bottom">
|
||||
<code
|
||||
className={classnames({
|
||||
'kapacitor-metric-placeholder': !statement,
|
||||
'metric-placeholder': !statement,
|
||||
})}
|
||||
>
|
||||
{statement || 'Build a query below'}
|
||||
|
|
|
@ -83,7 +83,7 @@ class KapacitorForm extends Component {
|
|||
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<button
|
||||
className="btn btn-info"
|
||||
className="btn btn-default"
|
||||
type="button"
|
||||
onClick={onReset}
|
||||
>
|
||||
|
|
|
@ -70,6 +70,8 @@ export const KapacitorRule = React.createClass({
|
|||
source={source}
|
||||
query={query}
|
||||
actions={queryActions}
|
||||
onAddEvery={this.handleAddEvery}
|
||||
onRemoveEvery={this.handleRemoveEvery}
|
||||
/>
|
||||
<ValuesSection
|
||||
rule={rule}
|
||||
|
@ -149,6 +151,16 @@ export const KapacitorRule = React.createClass({
|
|||
})
|
||||
},
|
||||
|
||||
handleAddEvery(frequency) {
|
||||
const {rule: {id: ruleID}, kapacitorActions: {addEvery}} = this.props
|
||||
addEvery(ruleID, frequency)
|
||||
},
|
||||
|
||||
handleRemoveEvery() {
|
||||
const {rule: {id: ruleID}, kapacitorActions: {removeEvery}} = this.props
|
||||
removeEvery(ruleID)
|
||||
},
|
||||
|
||||
validationError() {
|
||||
const {rule, query} = this.props
|
||||
if (rule.trigger === 'deadman') {
|
||||
|
|
|
@ -33,7 +33,7 @@ export const RuleGraph = React.createClass({
|
|||
|
||||
if (!queryText) {
|
||||
return (
|
||||
<div className="rule-preview--graph-empty">
|
||||
<div className="rule-builder--graph-empty">
|
||||
<p>Select a <strong>Time-Series</strong> to preview on a graph</p>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -89,7 +89,7 @@ export const RuleHeader = React.createClass({
|
|||
id="save-kapacitor-tooltip"
|
||||
effect="solid"
|
||||
html={true}
|
||||
offset={{top: 2}}
|
||||
offset={{bottom: 4}}
|
||||
place="bottom"
|
||||
class="influx-tooltip kapacitor-tooltip place-bottom"
|
||||
/>
|
||||
|
@ -110,31 +110,31 @@ export const RuleHeader = React.createClass({
|
|||
onKeyDown={e => this.handleEditName(e, rule)}
|
||||
onBlur={() => this.handleEditNameBlur(rule)}
|
||||
placeholder="Name your rule"
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
/>
|
||||
: <h1
|
||||
className="page-header__title page-header--editable kapacitor-theme"
|
||||
onClick={this.toggleEditName}
|
||||
data-for="rename-kapacitor-tooltip"
|
||||
data-tip="Click to Rename"
|
||||
>
|
||||
{rule.name}
|
||||
<span className="icon pencil" />
|
||||
<ReactTooltip
|
||||
id="rename-kapacitor-tooltip"
|
||||
delayShow={200}
|
||||
effect="solid"
|
||||
html={true}
|
||||
offset={{top: 2}}
|
||||
place="bottom"
|
||||
class="influx-tooltip kapacitor-tooltip place-bottom"
|
||||
/>
|
||||
</h1>
|
||||
: <div className="page-header__left">
|
||||
<h1
|
||||
className="page-header__title page-header--editable kapacitor-theme"
|
||||
onClick={this.toggleEditName}
|
||||
data-for="rename-kapacitor-tooltip"
|
||||
data-tip="Click to Rename"
|
||||
>
|
||||
{rule.name}
|
||||
<span className="icon pencil" />
|
||||
<ReactTooltip
|
||||
id="rename-kapacitor-tooltip"
|
||||
delayShow={200}
|
||||
effect="solid"
|
||||
html={true}
|
||||
offset={{top: 2}}
|
||||
place="bottom"
|
||||
class="influx-tooltip kapacitor-tooltip place-bottom"
|
||||
/>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div className="page-header__left">
|
||||
{name}
|
||||
</div>
|
||||
)
|
||||
return name
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -53,16 +53,16 @@ export const RuleMessage = React.createClass({
|
|||
const selectedAlert = rule.alerts[0] || alerts[0].text
|
||||
|
||||
return (
|
||||
<div className="kapacitor-rule-section">
|
||||
<h3 className="rule-section-heading">Alert Message</h3>
|
||||
<div className="rule-section-body">
|
||||
<div className="kapacitor-values-tabs">
|
||||
<div className="rule-section">
|
||||
<h3 className="rule-section--heading">Alert Message</h3>
|
||||
<div className="rule-section--body">
|
||||
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
|
||||
<p>Send this Alert to:</p>
|
||||
<ul className="btn-group btn-group-lg tab-group">
|
||||
<ul className="nav nav-tablist nav-tablist-sm nav-tablist-malachite">
|
||||
{alerts.map(alert => (
|
||||
<li
|
||||
key={alert.text}
|
||||
className={classnames('btn tab', {
|
||||
className={classnames({
|
||||
active: alert.text === selectedAlert,
|
||||
})}
|
||||
onClick={() => this.handleChooseAlert(alert)}
|
||||
|
@ -78,25 +78,27 @@ export const RuleMessage = React.createClass({
|
|||
rule={rule}
|
||||
/>
|
||||
{selectedAlert === 'smtp'
|
||||
? <div className="alert-message--email-body">
|
||||
? <div className="rule-section--border-bottom">
|
||||
<textarea
|
||||
className="alert-text details"
|
||||
className="form-control form-malachite monotype rule-builder--message"
|
||||
placeholder="Email body text goes here"
|
||||
ref={r => (this.details = r)}
|
||||
onChange={() =>
|
||||
actions.updateDetails(rule.id, this.details.value)}
|
||||
value={rule.details}
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
: null}
|
||||
<textarea
|
||||
className="alert-text message"
|
||||
className="form-control form-malachite monotype rule-builder--message"
|
||||
ref={r => (this.message = r)}
|
||||
onChange={() => actions.updateMessage(rule.id, this.message.value)}
|
||||
placeholder="Example: {{ .ID }} is {{ .Level }} value: {{ index .Fields "value" }}"
|
||||
value={rule.message}
|
||||
spellCheck={false}
|
||||
/>
|
||||
<div className="alert-message--formatting">
|
||||
<div className="rule-section--row rule-section--row-last rule-section--border-top">
|
||||
<p>Templates:</p>
|
||||
{Object.keys(templates).map(t => {
|
||||
return (
|
||||
|
@ -137,7 +139,15 @@ const CodeData = React.createClass({
|
|||
const {onClickTemplate, template} = this.props
|
||||
const {label, text} = template
|
||||
|
||||
return <code data-tip={text} onClick={onClickTemplate}>{label}</code>
|
||||
return (
|
||||
<code
|
||||
className="rule-builder--message-template"
|
||||
data-tip={text}
|
||||
onClick={onClickTemplate}
|
||||
>
|
||||
{label}
|
||||
</code>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -14,11 +14,12 @@ const RuleMessageAlertConfig = ({updateAlertNodes, alert, rule}) => {
|
|||
return null
|
||||
}
|
||||
return (
|
||||
<div className="rule-section--item alert-message--config">
|
||||
<div className="rule-section--row rule-section--border-bottom">
|
||||
<p>{DEFAULT_ALERT_LABELS[alert]}</p>
|
||||
<input
|
||||
id="alert-input"
|
||||
className="form-control size-486 form-control--green input-sm"
|
||||
className="form-control input-sm form-malachite"
|
||||
style={{flex: '1 0 0'}}
|
||||
type="text"
|
||||
placeholder={DEFAULT_ALERT_PLACEHOLDERS[alert]}
|
||||
onChange={e => updateAlertNodes(rule.id, alert, e.target.value)}
|
||||
|
|
|
@ -22,12 +22,14 @@ export const ValuesSection = React.createClass({
|
|||
const initialIndex = TABS.indexOf(_.startCase(rule.trigger))
|
||||
|
||||
return (
|
||||
<div className="kapacitor-rule-section">
|
||||
<h3 className="rule-section-heading">Rule Conditions</h3>
|
||||
<div className="rule-section-body">
|
||||
<div className="rule-section">
|
||||
<h3 className="rule-section--heading">Rule Conditions</h3>
|
||||
<div className="rule-section--body">
|
||||
<Tabs initialIndex={initialIndex} onSelect={this.handleChooseTrigger}>
|
||||
<TabList isKapacitorTabs="true">
|
||||
{TABS.map(tab => <Tab key={tab}>{tab}</Tab>)}
|
||||
{TABS.map(tab => (
|
||||
<Tab key={tab} isKapacitorTab={true}>{tab}</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
|
@ -99,29 +101,38 @@ const Threshold = React.createClass({
|
|||
const operators = mapToItems(OPERATORS, 'operator')
|
||||
|
||||
return (
|
||||
<div className="value-selector">
|
||||
<div className="rule-section--row rule-section--border-bottom">
|
||||
<p>Send Alert where</p>
|
||||
<span>
|
||||
<span className="rule-builder--metric">
|
||||
{query.fields.length ? query.fields[0].field : 'Select a Time-Series'}
|
||||
</span>
|
||||
<p>is</p>
|
||||
<Dropdown
|
||||
className="size-176 dropdown-kapacitor"
|
||||
className="dropdown-180"
|
||||
menuClass="dropdown-malachite"
|
||||
items={operators}
|
||||
selected={operator}
|
||||
onChoose={this.handleDropdownChange}
|
||||
/>
|
||||
<input
|
||||
className="form-control input-sm size-166 form-control--green"
|
||||
className="form-control input-sm form-malachite monotype"
|
||||
style={{width: '160px'}}
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
ref={r => (this.valueInput = r)}
|
||||
defaultValue={value}
|
||||
onKeyUp={this.handleInputChange}
|
||||
placeholder={
|
||||
operator === 'inside range' || operator === 'outside range'
|
||||
? 'Lower'
|
||||
: null
|
||||
}
|
||||
/>
|
||||
{(operator === 'inside range' || operator === 'outside range') &&
|
||||
<input
|
||||
className="form-control input-sm size-166 form-control--green"
|
||||
className="form-control input-sm form-malachite monotype"
|
||||
style={{width: '160px'}}
|
||||
placeholder="Upper"
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
ref={r => (this.valueRangeInput = r)}
|
||||
|
@ -162,30 +173,34 @@ const Relative = React.createClass({
|
|||
const operators = mapToItems(OPERATORS, 'operator')
|
||||
|
||||
return (
|
||||
<div className="value-selector">
|
||||
<div className="rule-section--row rule-section--border-bottom">
|
||||
<p>Send Alert when</p>
|
||||
<Dropdown
|
||||
className="size-106 dropdown-kapacitor"
|
||||
className="dropdown-110"
|
||||
menuClass="dropdown-malachite"
|
||||
items={changes}
|
||||
selected={change}
|
||||
onChoose={this.handleDropdownChange}
|
||||
/>
|
||||
<p>compared to previous</p>
|
||||
<Dropdown
|
||||
className="size-66 dropdown-kapacitor"
|
||||
className="dropdown-80"
|
||||
menuClass="dropdown-malachite"
|
||||
items={shifts}
|
||||
selected={shift}
|
||||
onChoose={this.handleDropdownChange}
|
||||
/>
|
||||
<p>is</p>
|
||||
<Dropdown
|
||||
className="size-176 dropdown-kapacitor"
|
||||
className="dropdown-160"
|
||||
menuClass="dropdown-malachite"
|
||||
items={operators}
|
||||
selected={operator}
|
||||
onChoose={this.handleDropdownChange}
|
||||
/>
|
||||
<input
|
||||
className="form-control input-sm size-166 form-control--green"
|
||||
className="form-control input-sm form-malachite monotype"
|
||||
style={{width: '160px'}}
|
||||
ref={r => (this.input = r)}
|
||||
defaultValue={value}
|
||||
onKeyUp={this.handleInputChange}
|
||||
|
@ -193,7 +208,7 @@ const Relative = React.createClass({
|
|||
type="text"
|
||||
spellCheck="false"
|
||||
/>
|
||||
<p>{change === CHANGES[1] ? '%' : ''}</p>
|
||||
{change === CHANGES[1] ? <p>%</p> : null}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
@ -219,10 +234,11 @@ const Deadman = React.createClass({
|
|||
})
|
||||
|
||||
return (
|
||||
<div className="value-selector">
|
||||
<div className="rule-section--row">
|
||||
<p>Send Alert if Data is missing for</p>
|
||||
<Dropdown
|
||||
className="size-66 dropdown-kapacitor"
|
||||
className="dropdown-80"
|
||||
menuClass="dropdown-malachite"
|
||||
items={periods}
|
||||
selected={this.props.rule.values.period}
|
||||
onChoose={this.handleChange}
|
||||
|
|
|
@ -14,18 +14,18 @@ class RedactedInput extends Component {
|
|||
|
||||
if (defaultValue === true && !editing) {
|
||||
return (
|
||||
<div className="alert-value-set">
|
||||
<span>
|
||||
value set
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => {
|
||||
this.setState({editing: true})
|
||||
}}
|
||||
>
|
||||
(change it)
|
||||
</a>
|
||||
<div className="form-control-static redacted-input">
|
||||
<span className="alert-value-set">
|
||||
<span className="icon checkmark" /> Value set
|
||||
</span>
|
||||
<button
|
||||
className="btn btn-xs btn-link"
|
||||
onClick={() => {
|
||||
this.setState({editing: true})
|
||||
}}
|
||||
>
|
||||
Change
|
||||
</button>
|
||||
<input
|
||||
className="form-control"
|
||||
id={id}
|
||||
|
|
|
@ -52,18 +52,21 @@ const TelegramConfig = React.createClass({
|
|||
|
||||
return (
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<p className="no-user-select">
|
||||
You need a
|
||||
{' '}
|
||||
<a
|
||||
href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot"
|
||||
target="_blank"
|
||||
>
|
||||
Telegram Bot
|
||||
</a>
|
||||
{' '}
|
||||
to use this endpoint
|
||||
</p>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="alert alert-warning alert-icon no-user-select">
|
||||
<span className="icon triangle" />
|
||||
You need a
|
||||
{' '}
|
||||
<a
|
||||
href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot"
|
||||
target="_blank"
|
||||
>
|
||||
Telegram Bot
|
||||
</a>
|
||||
{' '}
|
||||
to use this endpoint
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">
|
||||
Token
|
||||
|
@ -100,7 +103,7 @@ const TelegramConfig = React.createClass({
|
|||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="parseMode">Select the alert message format</label>
|
||||
<div className="form-control-static">
|
||||
<div className="radio">
|
||||
<div className="radio-item">
|
||||
<input
|
||||
id="parseModeMarkdown"
|
||||
type="radio"
|
||||
|
@ -111,7 +114,7 @@ const TelegramConfig = React.createClass({
|
|||
/>
|
||||
<label htmlFor="parseModeMarkdown">Markdown</label>
|
||||
</div>
|
||||
<div className="radio">
|
||||
<div className="radio-item">
|
||||
<input
|
||||
id="parseModeHTML"
|
||||
type="radio"
|
||||
|
|
|
@ -19,6 +19,8 @@ export const defaultRuleConfigs = {
|
|||
},
|
||||
}
|
||||
|
||||
export const defaultEveryFrequency = '30s'
|
||||
|
||||
export const OPERATORS = [
|
||||
'greater than',
|
||||
'equal to or greater',
|
||||
|
@ -80,7 +82,7 @@ export const DEFAULT_ALERT_LABELS = {
|
|||
http: 'URL:',
|
||||
tcp: 'Address:',
|
||||
exec: 'Add Command (Arguments separated by Spaces):',
|
||||
log: 'File',
|
||||
log: 'File:',
|
||||
smtp: 'Email Addresses (Separated by Spaces):',
|
||||
slack: 'Send alerts to Slack channel:',
|
||||
alerta: 'Paste Alerta TICKscript:',
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
import * as kapacitorActionCreators from '../actions/view'
|
||||
import * as queryActionCreators from '../../data_explorer/actions/view'
|
||||
|
||||
import * as kapacitorActionCreators from 'src/kapacitor/actions/view'
|
||||
import * as queryActionCreators from 'src/data_explorer/actions/view'
|
||||
|
||||
import {bindActionCreators} from 'redux'
|
||||
import {getActiveKapacitor, getKapacitorConfig} from 'shared/apis/index'
|
||||
import {ALERTS, DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||
|
@ -23,6 +25,8 @@ export const KapacitorRulePage = React.createClass({
|
|||
loadDefaultRule: PropTypes.func.isRequired,
|
||||
fetchRule: PropTypes.func.isRequired,
|
||||
chooseTrigger: PropTypes.func.isRequired,
|
||||
addEvery: PropTypes.func.isRequired,
|
||||
removeEvery: PropTypes.func.isRequired,
|
||||
updateRuleValues: PropTypes.func.isRequired,
|
||||
updateMessage: PropTypes.func.isRequired,
|
||||
updateAlerts: PropTypes.func.isRequired,
|
||||
|
@ -76,7 +80,7 @@ export const KapacitorRulePage = React.createClass({
|
|||
.catch(() => {
|
||||
addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'We couldn\'t find a configured Kapacitor for this source',
|
||||
text: "We couldn't find a configured Kapacitor for this source", // eslint-disable-line quotes
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -15,7 +15,7 @@ export default function rules(state = {}, action) {
|
|||
message: '',
|
||||
alerts: [],
|
||||
alertNodes: [],
|
||||
every: '30s',
|
||||
every: null,
|
||||
name: 'Untitled Rule',
|
||||
},
|
||||
})
|
||||
|
@ -45,6 +45,16 @@ export default function rules(state = {}, action) {
|
|||
})
|
||||
}
|
||||
|
||||
case 'ADD_EVERY': {
|
||||
const {ruleID, frequency} = action.payload
|
||||
return {...state, [ruleID]: {...state[ruleID], every: frequency}}
|
||||
}
|
||||
|
||||
case 'REMOVE_EVERY': {
|
||||
const {ruleID} = action.payload
|
||||
return {...state, [ruleID]: {...state[ruleID], every: null}}
|
||||
}
|
||||
|
||||
case 'UPDATE_RULE_VALUES': {
|
||||
const {ruleID, trigger, values} = action.payload
|
||||
return Object.assign({}, state, {
|
||||
|
|
|
@ -46,7 +46,7 @@ const AutoRefreshDropdown = React.createClass({
|
|||
return (
|
||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||
<div
|
||||
className="btn btn-sm btn-info dropdown-toggle"
|
||||
className="btn btn-sm btn-default dropdown-toggle"
|
||||
onClick={() => self.toggleMenu()}
|
||||
>
|
||||
<span
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const ConfirmButtons = ({onConfirm, item, onCancel}) => (
|
||||
const ConfirmButtons = ({onConfirm, item, onCancel, buttonSize}) => (
|
||||
<div className="confirm-buttons">
|
||||
<button className="btn btn-xs btn-info" onClick={() => onCancel(item)}>
|
||||
<button
|
||||
className={classnames('btn btn-info btn-square', {
|
||||
[buttonSize]: buttonSize,
|
||||
})}
|
||||
onClick={() => onCancel(item)}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
<button className="btn btn-xs btn-success" onClick={() => onConfirm(item)}>
|
||||
<button
|
||||
className={classnames('btn btn-success btn-square', {
|
||||
[buttonSize]: buttonSize,
|
||||
})}
|
||||
onClick={() => onConfirm(item)}
|
||||
>
|
||||
<span className="icon checkmark" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -17,6 +28,10 @@ ConfirmButtons.propTypes = {
|
|||
onConfirm: func.isRequired,
|
||||
item: oneOfType([shape(), string]),
|
||||
onCancel: func.isRequired,
|
||||
buttonSize: string,
|
||||
}
|
||||
|
||||
ConfirmButtons.defaultProps = {
|
||||
buttonSize: 'btn-sm',
|
||||
}
|
||||
export default ConfirmButtons
|
||||
|
|
|
@ -46,15 +46,15 @@ class CustomTimeRangeDropdown extends Component {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classnames('custom-time-range', {show: isVisible})}
|
||||
className={classnames('custom-time-range', {open: isVisible})}
|
||||
style={{display: 'flex'}}
|
||||
>
|
||||
<button
|
||||
className="btn btn-sm btn-info custom-time-range--btn"
|
||||
className="btn btn-sm btn-default dropdown-toggle"
|
||||
onClick={onToggle}
|
||||
>
|
||||
<span className="icon clock" />
|
||||
{`${moment(lower).format('MMM Do HH:mm')} — ${moment(upper).format('MMM Do HH:mm')}`}
|
||||
<span className="dropdown-selected">{`${moment(lower).format('MMM Do HH:mm')} — ${moment(upper).format('MMM Do HH:mm')}`}</span>
|
||||
<span className="caret" />
|
||||
</button>
|
||||
<div className="custom-time--container">
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||
|
||||
const DeleteButton = ({onClickDelete}) => (
|
||||
const DeleteButton = ({onClickDelete, buttonSize}) => (
|
||||
<button
|
||||
className="btn btn-xs btn-danger admin-table--hidden"
|
||||
className={classnames('btn btn-danger table--show-on-row-hover', {
|
||||
[buttonSize]: buttonSize,
|
||||
})}
|
||||
onClick={onClickDelete}
|
||||
>
|
||||
Delete
|
||||
|
@ -35,7 +38,7 @@ class DeleteConfirmButtons extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {onDelete, item} = this.props
|
||||
const {onDelete, item, buttonSize} = this.props
|
||||
const {isConfirming} = this.state
|
||||
|
||||
return isConfirming
|
||||
|
@ -43,8 +46,12 @@ class DeleteConfirmButtons extends Component {
|
|||
onConfirm={onDelete}
|
||||
item={item}
|
||||
onCancel={this.handleCancel}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
: <DeleteButton
|
||||
onClickDelete={this.handleClickDelete}
|
||||
buttonSize={buttonSize}
|
||||
/>
|
||||
: <DeleteButton onClickDelete={this.handleClickDelete} />
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,11 +59,17 @@ const {func, oneOfType, shape, string} = PropTypes
|
|||
|
||||
DeleteButton.propTypes = {
|
||||
onClickDelete: func.isRequired,
|
||||
buttonSize: string,
|
||||
}
|
||||
|
||||
DeleteConfirmButtons.propTypes = {
|
||||
item: oneOfType([(string, shape())]),
|
||||
onDelete: func.isRequired,
|
||||
buttonSize: string,
|
||||
}
|
||||
|
||||
DeleteConfirmButtons.defaultProps = {
|
||||
buttonSize: 'btn-sm',
|
||||
}
|
||||
|
||||
export default OnClickOutside(DeleteConfirmButtons)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react'
|
||||
|
||||
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
|
||||
import {ADMIN_TABLE} from 'src/admin/constants/tableSizing'
|
||||
|
||||
const DeleteConfirmTableCell = props => (
|
||||
<td className="text-right" style={{width: '85px'}}>
|
||||
<td className="text-right" style={{width: `${ADMIN_TABLE.colDelete}px`}}>
|
||||
<DeleteConfirmButtons {...props} />
|
||||
</td>
|
||||
)
|
||||
|
|
|
@ -3,10 +3,7 @@ import {Link} from 'react-router'
|
|||
import classnames from 'classnames'
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import {
|
||||
DROPDOWN_MENU_MAX_HEIGHT,
|
||||
DROPDOWN_MENU_ITEM_THRESHOLD,
|
||||
} from 'shared/constants/index'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
|
||||
class Dropdown extends Component {
|
||||
constructor(props) {
|
||||
|
@ -32,7 +29,7 @@ class Dropdown extends Component {
|
|||
static defaultProps = {
|
||||
actions: [],
|
||||
buttonSize: 'btn-sm',
|
||||
buttonColor: 'btn-info',
|
||||
buttonColor: 'btn-default',
|
||||
menuWidth: '100%',
|
||||
useAutoComplete: false,
|
||||
}
|
||||
|
@ -126,13 +123,14 @@ class Dropdown extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
renderShortMenu() {
|
||||
renderMenu() {
|
||||
const {
|
||||
actions,
|
||||
addNew,
|
||||
items,
|
||||
menuWidth,
|
||||
menuLabel,
|
||||
menuClass,
|
||||
useAutoComplete,
|
||||
selected,
|
||||
} = this.props
|
||||
|
@ -143,86 +141,19 @@ class Dropdown extends Component {
|
|||
<ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
style={{width: menuWidth}}
|
||||
>
|
||||
{menuLabel ? <li className="dropdown-header">{menuLabel}</li> : null}
|
||||
{menuItems.map((item, i) => {
|
||||
if (item.text === 'SEPARATOR') {
|
||||
return <li key={i} role="separator" className="divider" />
|
||||
}
|
||||
return (
|
||||
<li
|
||||
className={classnames('dropdown-item', {
|
||||
highlight: i === highlightedItemIndex,
|
||||
active: item.text === selected,
|
||||
})}
|
||||
key={i}
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => this.handleSelection(item)}
|
||||
onMouseOver={() => this.handleHighlight(i)}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
{actions.length > 0
|
||||
? <div className="dropdown-item__actions">
|
||||
{actions.map(action => {
|
||||
return (
|
||||
<button
|
||||
key={action.text}
|
||||
className="dropdown-item__action"
|
||||
onClick={e => this.handleAction(e, action, item)}
|
||||
>
|
||||
<span
|
||||
title={action.text}
|
||||
className={`icon ${action.icon}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
: null}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
{addNew
|
||||
? <li className="dropdown-item">
|
||||
<Link to={addNew.url}>
|
||||
{addNew.text}
|
||||
</Link>
|
||||
</li>
|
||||
: null}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
renderLongMenu() {
|
||||
const {
|
||||
actions,
|
||||
addNew,
|
||||
items,
|
||||
menuWidth,
|
||||
menuLabel,
|
||||
useAutoComplete,
|
||||
selected,
|
||||
} = this.props
|
||||
const {filteredItems, highlightedItemIndex} = this.state
|
||||
const menuItems = useAutoComplete ? filteredItems : items
|
||||
|
||||
return (
|
||||
<ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
})}
|
||||
style={{width: menuWidth, height: DROPDOWN_MENU_MAX_HEIGHT}}
|
||||
>
|
||||
<FancyScrollbar autoHide={false}>
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
{menuLabel ? <li className="dropdown-header">{menuLabel}</li> : null}
|
||||
{menuItems.map((item, i) => {
|
||||
if (item.text === 'SEPARATOR') {
|
||||
return <li key={i} role="separator" className="divider" />
|
||||
return <li key={i} className="dropdown-divider" />
|
||||
}
|
||||
return (
|
||||
<li
|
||||
|
@ -240,12 +171,12 @@ class Dropdown extends Component {
|
|||
{item.text}
|
||||
</a>
|
||||
{actions.length > 0
|
||||
? <div className="dropdown-item__actions">
|
||||
? <div className="dropdown-actions">
|
||||
{actions.map(action => {
|
||||
return (
|
||||
<button
|
||||
key={action.text}
|
||||
className="dropdown-item__action"
|
||||
className="dropdown-action"
|
||||
onClick={e => this.handleAction(e, action, item)}
|
||||
>
|
||||
<span
|
||||
|
@ -261,8 +192,8 @@ class Dropdown extends Component {
|
|||
)
|
||||
})}
|
||||
{addNew
|
||||
? <li>
|
||||
<Link to={addNew.url}>
|
||||
? <li className="multi-select--apply">
|
||||
<Link className="btn btn-xs btn-default" to={addNew.url}>
|
||||
{addNew.text}
|
||||
</Link>
|
||||
</li>
|
||||
|
@ -277,6 +208,7 @@ class Dropdown extends Component {
|
|||
items,
|
||||
selected,
|
||||
className,
|
||||
menuClass,
|
||||
iconName,
|
||||
buttonSize,
|
||||
buttonColor,
|
||||
|
@ -288,7 +220,10 @@ class Dropdown extends Component {
|
|||
return (
|
||||
<div
|
||||
onClick={this.handleClick}
|
||||
className={classnames(`dropdown ${className}`, {open: isOpen})}
|
||||
className={classnames('dropdown', {
|
||||
open: isOpen,
|
||||
[className]: className,
|
||||
})}
|
||||
>
|
||||
{useAutoComplete && isOpen
|
||||
? <div
|
||||
|
@ -314,14 +249,14 @@ class Dropdown extends Component {
|
|||
<span className="dropdown-selected">{selected}</span>
|
||||
<span className="caret" />
|
||||
</div>}
|
||||
{isOpen && menuItems.length < DROPDOWN_MENU_ITEM_THRESHOLD
|
||||
? this.renderShortMenu()
|
||||
: null}
|
||||
{isOpen && menuItems.length >= DROPDOWN_MENU_ITEM_THRESHOLD
|
||||
? this.renderLongMenu()
|
||||
: null}
|
||||
{isOpen && menuItems.length ? this.renderMenu() : null}
|
||||
{isOpen && !menuItems.length
|
||||
? <ul className="dropdown-menu">
|
||||
? <ul
|
||||
className={classnames('dropdown-menu', {
|
||||
'dropdown-menu--no-highlight': useAutoComplete,
|
||||
[menuClass]: menuClass,
|
||||
})}
|
||||
>
|
||||
<li className="dropdown-empty">No matching items</li>
|
||||
</ul>
|
||||
: null}
|
||||
|
@ -358,6 +293,7 @@ Dropdown.propTypes = {
|
|||
buttonColor: string,
|
||||
menuWidth: string,
|
||||
menuLabel: string,
|
||||
menuClass: string,
|
||||
useAutoComplete: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -9,21 +9,34 @@ class FancyScrollbar extends Component {
|
|||
|
||||
static defaultProps = {
|
||||
autoHide: true,
|
||||
autoHeight: false,
|
||||
}
|
||||
|
||||
render() {
|
||||
const {autoHide, children, className} = this.props
|
||||
const {autoHide, autoHeight, children, className, maxHeight} = this.props
|
||||
|
||||
return (
|
||||
<Scrollbars
|
||||
className={classnames('fancy-scroll--container', {[className]: className})}
|
||||
className={classnames('fancy-scroll--container', {
|
||||
[className]: className,
|
||||
})}
|
||||
autoHide={autoHide}
|
||||
autoHideTimeout={1000}
|
||||
autoHideDuration={250}
|
||||
renderTrackHorizontal={props => <div {...props} className="fancy-scroll--track-h"/>}
|
||||
renderTrackVertical={props => <div {...props} className="fancy-scroll--track-v"/>}
|
||||
renderThumbHorizontal={props => <div {...props} className="fancy-scroll--thumb-h"/>}
|
||||
renderThumbVertical={props => <div {...props} className="fancy-scroll--thumb-v"/>}
|
||||
autoHeight={autoHeight}
|
||||
autoHeightMax={maxHeight}
|
||||
renderTrackHorizontal={props => (
|
||||
<div {...props} className="fancy-scroll--track-h" />
|
||||
)}
|
||||
renderTrackVertical={props => (
|
||||
<div {...props} className="fancy-scroll--track-v" />
|
||||
)}
|
||||
renderThumbHorizontal={props => (
|
||||
<div {...props} className="fancy-scroll--thumb-h" />
|
||||
)}
|
||||
renderThumbVertical={props => (
|
||||
<div {...props} className="fancy-scroll--thumb-v" />
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Scrollbars>
|
||||
|
@ -31,12 +44,14 @@ class FancyScrollbar extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {bool, node, string} = PropTypes
|
||||
const {bool, node, number, string} = PropTypes
|
||||
|
||||
FancyScrollbar.propTypes = {
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
autoHide: bool,
|
||||
autoHeight: bool,
|
||||
maxHeight: number,
|
||||
}
|
||||
|
||||
export default FancyScrollbar
|
||||
|
|
|
@ -83,7 +83,7 @@ export const LayoutRenderer = React.createClass({
|
|||
return text
|
||||
},
|
||||
|
||||
renderRefreshingGraph(type, queries) {
|
||||
renderRefreshingGraph(type, queries, cellHeight) {
|
||||
const {timeRange, autoRefresh, templates} = this.props
|
||||
|
||||
if (type === 'single-stat') {
|
||||
|
@ -92,6 +92,7 @@ export const LayoutRenderer = React.createClass({
|
|||
queries={[queries[0]]}
|
||||
templates={templates}
|
||||
autoRefresh={autoRefresh}
|
||||
cellHeight={cellHeight}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ export const LayoutRenderer = React.createClass({
|
|||
shouldNotBeEditable={shouldNotBeEditable}
|
||||
cell={cell}
|
||||
>
|
||||
{this.renderRefreshingGraph(cell.type, queries)}
|
||||
{this.renderRefreshingGraph(cell.type, queries, cell.h)}
|
||||
</NameableGraph>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
|
||||
const labelText = ({localSelectedItems, isOpen, label}) => {
|
||||
if (label) {
|
||||
return label
|
||||
|
@ -67,20 +71,18 @@ class MultiSelectDropdown extends Component {
|
|||
|
||||
render() {
|
||||
const {localSelectedItems, isOpen} = this.state
|
||||
const {label} = this.props
|
||||
const {label, buttonSize, buttonColor, customClass, iconName} = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('dropdown multi-select-dropdown', {open: isOpen})}
|
||||
>
|
||||
<div className={classnames(`dropdown ${customClass}`, {open: isOpen})}>
|
||||
<div
|
||||
onClick={::this.toggleMenu}
|
||||
className="btn btn-xs btn-info dropdown-toggle"
|
||||
type="button"
|
||||
className={`dropdown-toggle btn ${buttonSize} ${buttonColor}`}
|
||||
>
|
||||
<div className="multi-select-dropdown__label">
|
||||
{iconName ? <span className={`icon ${iconName}`} /> : null}
|
||||
<span className="dropdown-selected">
|
||||
{labelText({localSelectedItems, isOpen, label})}
|
||||
</div>
|
||||
</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
{this.renderMenu()}
|
||||
|
@ -92,33 +94,36 @@ class MultiSelectDropdown extends Component {
|
|||
const {items} = this.props
|
||||
|
||||
return (
|
||||
<div className="dropdown-options">
|
||||
<div
|
||||
className="multi-select-dropdown__apply"
|
||||
onClick={this.onApplyFunctions}
|
||||
style={{listStyle: 'none'}}
|
||||
>
|
||||
<div className="btn btn-xs btn-info btn-block">Apply</div>
|
||||
</div>
|
||||
<ul
|
||||
className="dropdown-menu multi-select-dropdown__menu"
|
||||
aria-labelledby="dropdownMenu1"
|
||||
<ul className="dropdown-menu">
|
||||
<li className="multi-select--apply">
|
||||
<button
|
||||
className="btn btn-xs btn-info"
|
||||
onClick={this.onApplyFunctions}
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</li>
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
{items.map((listItem, i) => {
|
||||
return (
|
||||
<li
|
||||
key={i}
|
||||
className={classnames('multi-select-dropdown__item', {
|
||||
active: this.isSelected(listItem),
|
||||
className={classnames('multi-select--item', {
|
||||
checked: this.isSelected(listItem),
|
||||
})}
|
||||
onClick={_.wrap(listItem, this.onSelect)}
|
||||
>
|
||||
<a href="#">{listItem}</a>
|
||||
<div className="multi-select--checkbox" />
|
||||
<span>{listItem}</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +135,15 @@ MultiSelectDropdown.propTypes = {
|
|||
items: arrayOf(string.isRequired).isRequired,
|
||||
selectedItems: arrayOf(string.isRequired).isRequired,
|
||||
label: string,
|
||||
buttonSize: string,
|
||||
buttonColor: string,
|
||||
customClass: string,
|
||||
iconName: string,
|
||||
}
|
||||
MultiSelectDropdown.defaultProps = {
|
||||
buttonSize: 'btn-sm',
|
||||
buttonColor: 'btn-default',
|
||||
customClass: 'dropdown-160',
|
||||
}
|
||||
|
||||
export default OnClickOutside(MultiSelectDropdown)
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import shallowCompare from 'react-addons-shallow-compare'
|
||||
import lastValues from 'src/shared/parsing/lastValues'
|
||||
|
||||
const SMALL_CELL_HEIGHT = 1
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'LineGraph',
|
||||
propTypes: {
|
||||
data: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||
title: PropTypes.string,
|
||||
isFetchingInitially: PropTypes.bool,
|
||||
cellHeight: PropTypes.number,
|
||||
},
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
@ -15,7 +19,7 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {data} = this.props
|
||||
const {data, cellHeight} = this.props
|
||||
|
||||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||
if (this.props.isFetchingInitially) {
|
||||
|
@ -33,7 +37,13 @@ export default React.createClass({
|
|||
|
||||
return (
|
||||
<div className="single-stat">
|
||||
{roundedValue}
|
||||
<span
|
||||
className={classnames('single-stat--value', {
|
||||
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
|
||||
})}
|
||||
>
|
||||
{roundedValue}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -8,9 +8,20 @@ export const Tab = React.createClass({
|
|||
onClick: func,
|
||||
isDisabled: bool,
|
||||
isActive: bool,
|
||||
isKapacitorTab: bool,
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.props.isKapacitorTab) {
|
||||
return (
|
||||
<li
|
||||
className={classnames({active: this.props.isActive})}
|
||||
onClick={this.props.isDisabled ? null : this.props.onClick}
|
||||
>
|
||||
{this.props.children}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classnames('btn tab', {active: this.props.isActive})}
|
||||
|
@ -47,9 +58,11 @@ export const TabList = React.createClass({
|
|||
|
||||
if (this.props.isKapacitorTabs === 'true') {
|
||||
return (
|
||||
<div className="kapacitor-values-tabs">
|
||||
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
|
||||
<p>Alert Type</p>
|
||||
<div className="btn-group btn-group-lg tab-group">{children}</div>
|
||||
<div className="nav nav-tablist nav-tablist-sm nav-tablist-malachite">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import OnClickOutside from 'shared/components/OnClickOutside'
|
|||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import timeRanges from 'hson!../data/timeRanges.hson'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
|
||||
const TimeRangeDropdown = React.createClass({
|
||||
autobind: false,
|
||||
|
@ -58,7 +59,7 @@ const TimeRangeDropdown = React.createClass({
|
|||
return (
|
||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||
<div
|
||||
className="btn btn-sm btn-info dropdown-toggle"
|
||||
className="btn btn-sm btn-default dropdown-toggle"
|
||||
onClick={() => self.toggleMenu()}
|
||||
>
|
||||
<span className="icon clock" />
|
||||
|
@ -67,8 +68,12 @@ const TimeRangeDropdown = React.createClass({
|
|||
</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
<ul className="dropdown-menu" style={{height: '270px'}}>
|
||||
<FancyScrollbar>
|
||||
<ul className="dropdown-menu">
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
<li className="dropdown-header">Time Range</li>
|
||||
{timeRanges.map(item => {
|
||||
return (
|
||||
|
|
|
@ -1,21 +1,36 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const YesNoButtons = ({onConfirm, onCancel}) => (
|
||||
const YesNoButtons = ({onConfirm, onCancel, buttonSize}) => (
|
||||
<div>
|
||||
<button className="btn btn-xs btn-info" onClick={onCancel}>
|
||||
<button
|
||||
className={classnames('btn btn-square btn-info', {
|
||||
[buttonSize]: buttonSize,
|
||||
})}
|
||||
onClick={onCancel}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
<button className="btn btn-xs btn-success" onClick={onConfirm}>
|
||||
<button
|
||||
className={classnames('btn btn-square btn-success', {
|
||||
[buttonSize]: buttonSize,
|
||||
})}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
<span className="icon checkmark" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {func} = PropTypes
|
||||
const {func, string} = PropTypes
|
||||
|
||||
YesNoButtons.propTypes = {
|
||||
onConfirm: func.isRequired,
|
||||
onCancel: func.isRequired,
|
||||
buttonSize: string,
|
||||
}
|
||||
YesNoButtons.defaultProps = {
|
||||
buttonSize: 'btn-sm',
|
||||
}
|
||||
|
||||
export default YesNoButtons
|
||||
|
|
|
@ -379,8 +379,7 @@ export const STROKE_WIDTH = {
|
|||
light: 1.5,
|
||||
}
|
||||
|
||||
export const DROPDOWN_MENU_MAX_HEIGHT = '270px'
|
||||
export const DROPDOWN_MENU_ITEM_THRESHOLD = 10
|
||||
export const DROPDOWN_MENU_MAX_HEIGHT = 240
|
||||
|
||||
export const HEARTBEAT_INTERVAL = 10000 // ms
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ const kapacitorDropdown = (
|
|||
|
||||
return (
|
||||
<Dropdown
|
||||
className="dropdown-160"
|
||||
buttonColor="btn-info"
|
||||
className="dropdown-260"
|
||||
buttonColor="btn-default"
|
||||
buttonSize="btn-xs"
|
||||
items={kapacitorItems}
|
||||
onChoose={item => setActiveKapacitor(item.kapacitor)}
|
||||
|
@ -87,7 +87,7 @@ const InfluxTable = ({
|
|||
</Link>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<table className="table v-center margin-bottom-zero">
|
||||
<table className="table v-center margin-bottom-zero table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
@ -131,7 +131,7 @@ const InfluxTable = ({
|
|||
Connect
|
||||
</Link>}
|
||||
<button
|
||||
className="btn btn-danger btn-xs"
|
||||
className="btn btn-danger btn-xs btn-square"
|
||||
onClick={() => handleDeleteSource(s)}
|
||||
>
|
||||
<span className="icon trash" />
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
// Base Theme
|
||||
@import 'theme/bootstrap';
|
||||
@import 'theme/bootstrap-theme';
|
||||
@import 'theme/theme-dark';
|
||||
@import 'theme/reset';
|
||||
|
||||
// External
|
||||
|
@ -23,18 +22,16 @@
|
|||
@import 'layout/page-header';
|
||||
@import 'layout/sidebar';
|
||||
@import 'layout/overlay';
|
||||
@import 'layout/flash-messages';
|
||||
|
||||
// Components
|
||||
@import 'components/confirm-buttons';
|
||||
@import 'components/custom-time-range';
|
||||
@import 'components/dropdown';
|
||||
@import 'components/dygraphs';
|
||||
@import 'components/flash-messages';
|
||||
@import 'components/flip-toggle';
|
||||
@import 'components/graph-tips';
|
||||
@import 'components/graph';
|
||||
@import 'components/input-tag-list';
|
||||
@import 'components/multi-select-dropdown';
|
||||
@import 'components/page-header-dropdown';
|
||||
@import 'components/page-header-editable';
|
||||
@import 'components/page-spinner';
|
||||
|
|
|
@ -15,7 +15,4 @@
|
|||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
span.icon {
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
|
@ -6,16 +6,6 @@
|
|||
.custom-time-range {
|
||||
position: relative;
|
||||
}
|
||||
.btn.btn-sm.btn-info.custom-time-range--btn {
|
||||
padding: 0 30px 0 9px !important;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
right: 9px;
|
||||
top: calc(50% + 1px);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.custom-time--container {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
@ -242,8 +232,8 @@ $rd-cell-size: 30px;
|
|||
}
|
||||
|
||||
|
||||
/* Show State */
|
||||
.custom-time-range.show {
|
||||
/* Open State */
|
||||
.custom-time-range.open {
|
||||
.custom-time--container {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
/*
|
||||
Dropdowns
|
||||
----------------------------------------------
|
||||
Note: Some of these styles are intended to
|
||||
override styles from bootstrap-theme
|
||||
*/
|
||||
|
||||
$dropdown-menu-default-width: 100%;
|
||||
$dropdown-menu-max-height: 270px;
|
||||
|
||||
/*
|
||||
Generic width modifiers
|
||||
Use instead of creating new classes if possible
|
||||
*/
|
||||
.dropdown .dropdown-toggle,
|
||||
.dropdown .dropdown-autocomplete {
|
||||
width: 120px; /* Default width */
|
||||
}
|
||||
.dropdown {
|
||||
@for $i from 8 through 30 {
|
||||
&-#{$i * 10} .dropdown-toggle,
|
||||
&-#{$i * 10} .dropdown-autocomplete { width: #{$i * 10}px; }
|
||||
}
|
||||
}
|
||||
.dropdown-toggle {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: calc(50% + 1px);
|
||||
right: 8px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
> .icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.dropdown-selected {
|
||||
display: inline-block;
|
||||
flex: 1 0 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
.dropdown .dropdown-toggle.btn-xs {
|
||||
height: 22px !important;
|
||||
line-height: 22px !important;
|
||||
padding: 0 9px;
|
||||
}
|
||||
|
||||
/*
|
||||
AutoComplete Field
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-autocomplete {
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
|
||||
&.btn-xs {height: 22px;}
|
||||
&.btn-sm {height: 30px;}
|
||||
&.btn-md {height: 36px;}
|
||||
&.btn-lg {height: 50px;}
|
||||
}
|
||||
.dropdown-autocomplete--input {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
color: $g20-white;
|
||||
padding: 0;
|
||||
font-weight: 500;
|
||||
|
||||
.btn-xs & {padding: 0 18px 0 9px; font-size: 12px;}
|
||||
.btn-sm & {padding: 0 18px 0 9px; font-size: 13px;}
|
||||
.btn-md & {padding: 0 34px 0 17px; font-size: 14px;}
|
||||
.btn-lg & {padding: 0 48px 0 24px; font-size: 18px;}
|
||||
|
||||
&::-webkit-input-placeholder { color: rgba(255,255,255,0.5); font-weight: 500 !important; }
|
||||
&::-moz-placeholder { color: rgba(255,255,255,0.5); font-weight: 500 !important; }
|
||||
&:-ms-input-placeholder { color: rgba(255,255,255,0.5); font-weight: 500 !important; }
|
||||
&:-moz-placeholder { color: rgba(255,255,255,0.5); font-weight: 500 !important; }
|
||||
|
||||
&:focus {
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
.dropdown-empty {
|
||||
padding: 7px 9px;
|
||||
font-size: 13px;
|
||||
color: rgba(255,255,255,0.4);
|
||||
font-weight: 500;
|
||||
line-height: 15px;
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
/*
|
||||
Dropdown Menu
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-menu {
|
||||
width: $dropdown-menu-default-width;
|
||||
min-width: initial;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
max-height: $dropdown-menu-max-height;
|
||||
@include gradient-h($c-ocean, $c-pool);
|
||||
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.8);
|
||||
|
||||
li.dropdown-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
&.active {
|
||||
@include gradient-h($c-sapphire, $c-pool);
|
||||
}
|
||||
&:hover {
|
||||
@include gradient-h($c-laser, $c-pool);
|
||||
}
|
||||
}
|
||||
li.dropdown-item > a {
|
||||
position: relative;
|
||||
@include no-user-select();
|
||||
width: 100%;
|
||||
border-radius: 0 !important;
|
||||
display: inline-block;
|
||||
padding: 7px 9px;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
font-weight: 500 !important;
|
||||
color: $c-yeti !important;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
&:active,
|
||||
&:active:focus {
|
||||
@include gradient-h($c-sapphire, $c-pool);
|
||||
}
|
||||
&:focus {
|
||||
@include gradient-h($c-ocean, $c-pool);
|
||||
}
|
||||
}
|
||||
li.dropdown-item.highlight {
|
||||
&, &:hover {
|
||||
@include gradient-h($c-laser, $c-pool);
|
||||
}
|
||||
> a {
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropdown.dropdown-kapacitor .dropdown-toggle {
|
||||
color: $c-rainforest !important;
|
||||
&:hover {color: $c-honeydew !important;}
|
||||
}
|
||||
.dropdown.dropdown-kapacitor .dropdown-menu,
|
||||
.rule-builder .dropdown .dropdown-menu {
|
||||
@include custom-scrollbar($c-rainforest, $c-honeydew);
|
||||
@include gradient-h($c-pool, $c-rainforest);
|
||||
|
||||
li.dropdown-item:hover {
|
||||
@include gradient-h($c-laser, $c-rainforest);
|
||||
}
|
||||
li.dropdown-item > a {
|
||||
color: $c-mint !important;
|
||||
&:hover {
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
li.dropdown-item.highlight {
|
||||
&, &:hover {
|
||||
@include gradient-h($c-laser, $c-rainforest);
|
||||
}
|
||||
> a {
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropdown.dropdown-chronograf .dropdown-menu {
|
||||
@include custom-scrollbar($c-comet, $c-potassium);
|
||||
@include gradient-h($c-ocean, $c-comet);
|
||||
|
||||
li.dropdown-item:hover {
|
||||
@include gradient-h($c-laser, $c-comet);
|
||||
}
|
||||
li.dropdown-item > a {
|
||||
color: $c-twilight !important;
|
||||
&:hover {
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
li.dropdown-item.highlight {
|
||||
&, &:hover {
|
||||
@include gradient-h($c-laser, $c-comet);
|
||||
}
|
||||
> a {
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Dropdown Menu (only js highlighting, works with autocomplete feature)
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-menu.dropdown-menu--no-highlight {
|
||||
li.dropdown-item {
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
li.dropdown-item.highlight {
|
||||
&, &:hover {
|
||||
@include gradient-h($c-laser, $c-pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Dropdown Header
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-header {
|
||||
height: 32px;
|
||||
line-height: 30px;
|
||||
padding: 0 9px;
|
||||
white-space: nowrap;
|
||||
font-size: 14px !important;
|
||||
font-weight: 900;
|
||||
color: $c-neutrino !important;
|
||||
text-transform: none !important;
|
||||
border-bottom: 2px solid $c-pool;
|
||||
background-color: $c-ocean;
|
||||
|
||||
&:hover {
|
||||
background-image: none !important;
|
||||
background-color: $c-ocean !important;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Dropdown Actions
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-item__actions {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 3px;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.dropdown-item__action {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
border: none !important;
|
||||
font-size: 13px;
|
||||
transition:
|
||||
text-shadow 0.25s ease,
|
||||
color 0.25s ease;
|
||||
color: $c-sapphire;
|
||||
|
||||
&[data-target="#deleteExplorerModal"] .icon {
|
||||
position: relative;
|
||||
right: -1px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $g20-white;
|
||||
text-shadow:
|
||||
0 0 2px $c-hydrogen,
|
||||
0 0 3px $c-laser,
|
||||
0 0 6px $c-ocean;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Group By Time Dropdown
|
||||
----------------------------------------------
|
||||
*/
|
||||
.group-by-time-dropdown {
|
||||
.dropdown-toggle {
|
||||
width: 120px;
|
||||
height: 28px !important;
|
||||
line-height: 28px !important;
|
||||
font-size: 12px;
|
||||
text-transform: none;
|
||||
border-radius: $radius;
|
||||
display: block;
|
||||
}
|
||||
.dropdown-menu > li.dropdown-item > a {display: block;}
|
||||
}
|
||||
.data-explorer .group-by-time-dropdown .dropdown-toggle {
|
||||
font-weight: 600;
|
||||
background-color: $g2-kevlar;
|
||||
line-height: 24px !important;
|
||||
border: 2px solid $g6-smoke;
|
||||
transition:
|
||||
border-color 0.25s ease,
|
||||
color 0.25s ease,
|
||||
box-shadow 0.25s ease;
|
||||
color: $g10-wolf;
|
||||
|
||||
&:hover {
|
||||
border-color: $g7-graphite;
|
||||
color: $g18-cloud;
|
||||
}
|
||||
}
|
||||
.data-explorer .group-by-time-dropdown.open .dropdown-toggle {
|
||||
background-color: $g2-kevlar !important;
|
||||
border-color: $c-pool !important;
|
||||
box-shadow: 0 0 6px 0px $c-pool !important
|
||||
}
|
|
@ -138,22 +138,24 @@
|
|||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 60px;
|
||||
font-weight: 300;
|
||||
color: $c-pool;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
@include no-user-select();
|
||||
|
||||
&.graph-single-stat {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.single-stat--value {
|
||||
position: absolute;
|
||||
top: calc(50% - 15px);
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
font-size: 54px;
|
||||
line-height: 54px;
|
||||
font-weight: 300;
|
||||
color: $c-pool;
|
||||
|
||||
&.graph-single-stat {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
&.single-stat--small {
|
||||
font-size: 38px;
|
||||
line-height: 38px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
Styles for Flash Messages
|
||||
----------------------------------------------
|
||||
*/
|
||||
.flash-messages {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 570px;
|
||||
top: 36px;
|
||||
z-index: 9999;
|
||||
|
||||
.alert {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-width: 0;
|
||||
color: $g20-white;
|
||||
|
||||
&.alert-success {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
&.alert-primary {
|
||||
background-color: $c-pool;
|
||||
}
|
||||
&.alert-warning {
|
||||
background-color: $c-comet;
|
||||
|
||||
}
|
||||
&.alert-danger {
|
||||
background-color: $c-dreamsicle;
|
||||
}
|
||||
|
||||
button.close {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 16px;
|
||||
text-shadow: none;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
.icon {
|
||||
color: $g20-white;
|
||||
text-shadow: none;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
$ms-normal-left-padding: 9px;
|
||||
$ms-item-height: 26px;
|
||||
$ms-checkbox-size: 14px;
|
||||
$ms-checkbox-dot-size: 6px;
|
||||
$ms-checkbox-bg: $c-sapphire;
|
||||
$ms-checkbox-bg-hover: $c-ocean;
|
||||
$ms-checkbox-dot: $g20-white;
|
||||
|
||||
.multi-select-dropdown {
|
||||
.multi-select-dropdown__item > a {
|
||||
color: $c-neutrino !important;
|
||||
height: $ms-item-height;
|
||||
line-height: $ms-item-height;
|
||||
position: relative;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-right: $ms-normal-left-padding;
|
||||
padding-left: ($ms-normal-left-padding + $ms-checkbox-size + ($ms-normal-left-padding - 2px));
|
||||
|
||||
&,
|
||||
&:focus,
|
||||
&:active,
|
||||
&:active:focus {
|
||||
background: none !important;
|
||||
|
||||
&:hover {
|
||||
background: $c-pool;
|
||||
background: -moz-linear-gradient(left, $c-pool 0%, $c-pool 100%) !important;
|
||||
background: -webkit-linear-gradient(left, $c-pool 0%,$c-pool 100%) !important;
|
||||
background: linear-gradient(to right, $c-pool 0%,$c-pool 100%) !important;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$c-pool', endColorstr='$c-pool',GradientType=1 ) !important;
|
||||
}
|
||||
}
|
||||
/* Shared Checkbox Styles */
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
}
|
||||
/* Before = Checkbox */
|
||||
&:before {
|
||||
width: $ms-checkbox-size;
|
||||
height: $ms-checkbox-size;
|
||||
border-radius: $radius-small;
|
||||
background-color: $ms-checkbox-bg;
|
||||
left: $ms-normal-left-padding;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
/* After = Dot */
|
||||
&:after {
|
||||
width: $ms-checkbox-dot-size;
|
||||
height: $ms-checkbox-dot-size;
|
||||
background-color: $ms-checkbox-dot;
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
left: ($ms-normal-left-padding + ($ms-checkbox-size / 2));
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 0.25s ease;
|
||||
}
|
||||
/* Hover State */
|
||||
&:hover {
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
.dropdown-toggle {
|
||||
width: 110px;
|
||||
|
||||
&.btn-xs {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
&__apply {
|
||||
margin: 0;
|
||||
transition:
|
||||
opacity 0.25s ease;
|
||||
opacity: 0;
|
||||
@include gradient-h($c-ocean, $c-pool);
|
||||
border-radius: 3px 3px 0 0;
|
||||
padding: 9px;
|
||||
|
||||
& + .dropdown-menu {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
.dropdown-menu {
|
||||
opacity: 0;
|
||||
display: block !important;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
.dropdown-options {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
transition-property: all;
|
||||
}
|
||||
/* Styling Active State of items */
|
||||
.dropdown-menu > li.active > a {
|
||||
outline: none;
|
||||
&,
|
||||
&:focus,
|
||||
&:active,
|
||||
&:active:focus {
|
||||
color: $g20-white !important;
|
||||
background: $c-sapphire;
|
||||
background: -moz-linear-gradient(left, $c-sapphire 0%, $c-pool 100%) !important;
|
||||
background: -webkit-linear-gradient(left, $c-sapphire 0%,$c-pool 100%) !important;
|
||||
background: linear-gradient(to right, $c-sapphire 0%,$c-pool 100%) !important;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$c-sapphire', endColorstr='$c-pool',GradientType=1 ) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Checked State */
|
||||
.multi-select-dropdown li.multi-select-dropdown__item.active > a {
|
||||
&,
|
||||
&:focus,
|
||||
&:active,
|
||||
&:active:focus {
|
||||
background: none !important;
|
||||
}
|
||||
color: $g20-white !important;
|
||||
|
||||
&:after {
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
/* Open State */
|
||||
.multi-select-dropdown.open {
|
||||
.dropdown-options {
|
||||
visibility: visible;
|
||||
z-index: 9999;
|
||||
}
|
||||
.dropdown-menu,
|
||||
.multi-select-dropdown__apply {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.multi-select-dropdown__label {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding-right: 10px;
|
||||
position: absolute;
|
||||
width: calc(100% - #{($ms-normal-left-padding * 2)});
|
||||
left: $ms-normal-left-padding;
|
||||
}
|
|
@ -2,12 +2,14 @@
|
|||
.dropdown-toggle {
|
||||
height: 38px;
|
||||
padding-left: 0;
|
||||
padding-right: (11px + 12px); // caret width + offset
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
min-width: 50px;
|
||||
width: auto;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
text-transform: none;
|
||||
padding-right: (11px + 12px); // caret width + offset
|
||||
font-size: $page-header-size;
|
||||
font-weight: $page-header-weight;
|
||||
transition: color 0.25s ease;
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
font-weight: $page-header-weight;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
|
||||
&:focus {
|
||||
color: $c-pool;
|
||||
|
|
|
@ -114,9 +114,9 @@
|
|||
transition: color 0.25s ease;
|
||||
color: $g9-mountain;
|
||||
}
|
||||
& > input {
|
||||
& > input.form-control {
|
||||
order: 2;
|
||||
padding: 0 8px 0 24px;
|
||||
padding-left: 24px;
|
||||
border-color: $g6-smoke !important;
|
||||
|
||||
&:hover {border-color: $g7-graphite !important;}
|
||||
|
@ -172,13 +172,6 @@
|
|||
.query-builder--list-item {padding: 0;}
|
||||
.query-builder--filter {margin-bottom: 4px;}
|
||||
}
|
||||
/* Dropdowns inside query builder list items are forced to "xs" size */
|
||||
.query-builder--list-item .dropdown-toggle {
|
||||
height: 22px !important;
|
||||
line-height: 22px !important;
|
||||
font-size: 12px !important;
|
||||
width: 110px !important;
|
||||
}
|
||||
/* Toggle for grouping by tags in tags list */
|
||||
.group-by-tag {
|
||||
visibility: hidden;
|
||||
|
@ -198,9 +191,6 @@
|
|||
.query-builder--list-item.active .group-by-tag {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.query-builder--db-dropdown {
|
||||
display: inline-block;
|
||||
}
|
|
@ -88,14 +88,6 @@
|
|||
.dropdown.query-editor--templates {
|
||||
margin: 0 4px 0 0 ;
|
||||
|
||||
div.dropdown-toggle.btn.btn-sm {
|
||||
width: $query-editor--templates-width;
|
||||
padding: 0 9px !important;
|
||||
height: $query-editor--templates-height !important;
|
||||
line-height: $query-editor--templates-height !important;
|
||||
font-size: 12px;
|
||||
border-radius: $radius-small;
|
||||
}
|
||||
.dropdown-menu {
|
||||
left: initial;
|
||||
right: 0;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.query-maker {
|
||||
height: 100%;
|
||||
margin: 0 $explorer-page-padding;
|
||||
margin: 0 $page-wrapper-padding;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
.alert-value-set {
|
||||
padding: 6px 13px 0;
|
||||
|
||||
span a {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.redacted-input {
|
||||
height: 38px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.alert-value-set {
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
color: $c-rainforest;
|
||||
@include no-user-select();
|
||||
|
||||
.icon {margin-right: 5px;}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/*
|
||||
Stuff for making Tables of Data more readable
|
||||
Table Styles
|
||||
----------------------------------------------
|
||||
Most table styles are located in the Bootstrap
|
||||
Theme. Styles in here are for specific additions
|
||||
to tables that bootstrap does not do on its own
|
||||
*/
|
||||
|
||||
// table-custom class allows us to make a table from divs so we can use
|
||||
|
@ -43,58 +46,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
table {
|
||||
thead th {
|
||||
color: $g17-whisper !important;
|
||||
border-width: 1px;
|
||||
border-color: $g5-pepper !important;
|
||||
}
|
||||
tbody td {
|
||||
font-weight: 500;
|
||||
color: $g14-chromium !important;
|
||||
border: 0 !important;
|
||||
padding: 6px 8px !important;
|
||||
}
|
||||
tbody tr:hover {
|
||||
background-color: $g4-onyx;
|
||||
|
||||
td {
|
||||
color: $g19-ghost !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table .monotype {
|
||||
font-family: $code-font;
|
||||
letter-spacing: 0px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: $g9-mountain;
|
||||
}
|
||||
.table-dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: $g17-whisper;
|
||||
|
||||
&.dot-success {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
&.dot-primary {
|
||||
background-color: $c-pool;
|
||||
}
|
||||
&.dot-warning {
|
||||
background-color: $c-pineapple;
|
||||
}
|
||||
&.dot-danger {
|
||||
background-color: $c-dreamsicle;
|
||||
}
|
||||
&.dot-critical {
|
||||
background-color: $c-fire;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sortable Tables
|
||||
----------------------------------------------
|
||||
|
|
|
@ -36,7 +36,7 @@ $template-control-dropdown-min-width: 146px;
|
|||
@include no-user-select();
|
||||
white-space: nowrap;
|
||||
}
|
||||
.template-control--manage {
|
||||
button.btn.template-control--manage {
|
||||
margin: 7px 8px;
|
||||
}
|
||||
.template-control--controls {
|
||||
|
@ -64,38 +64,12 @@ $template-control-dropdown-min-width: 146px;
|
|||
margin: 0;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 0 $radius-small $radius-small;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-family: $code-font;
|
||||
}
|
||||
.dropdown-menu {
|
||||
@include gradient-h($c-star,$c-pool);
|
||||
|
||||
li.dropdown-item {
|
||||
&, &:hover {
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
&.active {@include gradient-h($c-amethyst,$c-pool);}
|
||||
}
|
||||
li.dropdown-item > a {
|
||||
&, &:focus {background: none;}
|
||||
&:active, &:active:focus {@include gradient-h($c-amethyst,$c-pool);}
|
||||
font-size: 12px;
|
||||
font-family: $code-font;
|
||||
}
|
||||
li.dropdown-item.highlight {
|
||||
&, &:hover {@include gradient-h($c-comet,$c-pool);}
|
||||
> a {
|
||||
color: $g20-white;
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.template-control--label {
|
||||
@include no-user-select();
|
||||
|
|
|
@ -191,11 +191,6 @@ $tvmp-table-gutter: 8px;
|
|||
|
||||
.btn-edit {
|
||||
order: 1;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
> span.icon {margin: 0 !important;}
|
||||
}
|
||||
|
||||
> .btn {margin-left: $tvmp-table-gutter;}
|
||||
|
@ -204,17 +199,9 @@ $tvmp-table-gutter: 8px;
|
|||
/* Janky, but doing this quick & dirty for now */
|
||||
.btn-danger {
|
||||
order: 2;
|
||||
height: 30px !important;
|
||||
line-height: 30px !important;
|
||||
padding: 0 9px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
.confirm-buttons > .btn {
|
||||
height: 30px !important;
|
||||
width: 30px !important;
|
||||
margin-left: $tvmp-table-gutter !important;
|
||||
font-size: 13px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
/* Hide the edit button when confirming a delete */
|
||||
.confirm-buttons + .btn-edit {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
Styles for Flash Messages
|
||||
----------------------------------------------
|
||||
*/
|
||||
.flash-messages {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 570px;
|
||||
top: 36px;
|
||||
z-index: 9999;
|
||||
}
|
|
@ -35,10 +35,6 @@ $page-header-weight: 400 !important;
|
|||
.page-header__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> *:only-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.page-header__left {
|
||||
justify-content: flex-start;
|
||||
|
@ -49,10 +45,15 @@ $page-header-weight: 400 !important;
|
|||
.page-header__right {
|
||||
justify-content: flex-end;
|
||||
> * {
|
||||
margin: 0 0 0 4px;
|
||||
margin: 0 0 0 4px !important;
|
||||
|
||||
&:only-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.page-header__title {
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
font-size: $page-header-size;
|
||||
font-weight: $page-header-weight;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
----------------------------------------------
|
||||
*/
|
||||
|
||||
$sidebar-width: 60px;
|
||||
$sidebar-menu-gutter: 30px;
|
||||
$sidebar-menu-indent: 13px;
|
||||
$sidebar-radius: 4px;
|
||||
|
||||
$sidebar-hover: $g8-storm;
|
||||
$sidebar-item-hover: $g7-graphite;
|
||||
|
@ -21,38 +21,6 @@ $sidebar-active-accent: $c-laser;
|
|||
$sidebar-logo-bg: $g17-whisper;
|
||||
$sidebar-logo-color: $g8-storm;
|
||||
|
||||
|
||||
// Rename button
|
||||
.js-open-rename-cluster-modal {
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: none;
|
||||
background-color: $g8-storm;
|
||||
vertical-align: middle;
|
||||
border-radius: 3px;
|
||||
margin-left: 11px;
|
||||
margin-right: -25px;
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
color: $g20-white;
|
||||
opacity: 0.4;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
opacity 0.25s ease;
|
||||
|
||||
.icon.pencil {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $g9-mountain;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar styles
|
||||
.sidebar {
|
||||
display: flex;
|
||||
|
@ -176,7 +144,7 @@ $sidebar-logo-color: $g8-storm;
|
|||
}
|
||||
|
||||
&__menu {
|
||||
border-radius: 0 $sidebar-radius $sidebar-radius 0;
|
||||
border-radius: 0 $radius $radius 0;
|
||||
background: $sidebar-hover;
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s ease;
|
||||
|
@ -237,12 +205,12 @@ $sidebar-logo-color: $g8-storm;
|
|||
|
||||
// Rounding top outside corner to match container
|
||||
&:first-child {
|
||||
border-top-right-radius: $sidebar-radius;
|
||||
border-top-right-radius: $radius;
|
||||
}
|
||||
|
||||
// Rounding bottom outside corner of match container
|
||||
&:last-child {
|
||||
border-bottom-right-radius: $sidebar-radius;
|
||||
border-bottom-right-radius: $radius;
|
||||
}
|
||||
|
||||
// Used for sub-navigation
|
||||
|
@ -314,5 +282,4 @@ $sidebar-logo-color: $g8-storm;
|
|||
font-weight: 400;
|
||||
padding: 0px $sidebar-menu-gutter;
|
||||
}
|
||||
|
||||
}
|
|
@ -109,6 +109,9 @@ $scrollbar-offset: 3px;
|
|||
-webkit-user-select: none !important;
|
||||
-ms-user-select: none !important;
|
||||
-o-user-select: none !important;
|
||||
&, &:hover {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
.no-user-select {
|
||||
user-select: none !important;
|
||||
|
@ -116,4 +119,7 @@ $scrollbar-offset: 3px;
|
|||
-webkit-user-select: none !important;
|
||||
-ms-user-select: none !important;
|
||||
-o-user-select: none !important;
|
||||
&, &:hover {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
$radius: 4px;
|
||||
$radius-small: 3px;
|
||||
|
||||
// Sidebar
|
||||
$sidebar-width: 60px;
|
||||
$page-wrapper-padding: 58px;
|
||||
$page-wrapper-padding: 60px;
|
||||
$page-wrapper-max-width: 1300px;
|
||||
$current-user-height: 56px;
|
||||
$cluster-switcher-height: 56px;
|
||||
$chronograf-page-header-height: 60px;
|
||||
$sidebar-tier1-height: 56px;
|
||||
|
||||
// Data Explorer
|
||||
$explorer-page-padding: $page-wrapper-padding;
|
||||
|
|
|
@ -12,22 +12,7 @@
|
|||
*/
|
||||
.admin-tabs {
|
||||
padding-right: 0;
|
||||
|
||||
& + div {
|
||||
padding-left: 0;
|
||||
|
||||
.panel {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
.panel-body {
|
||||
min-height: 300px;
|
||||
}
|
||||
.panel-title {
|
||||
font-size: 17px;
|
||||
font-weight: 400 !important;
|
||||
color: $g12-forge;
|
||||
}
|
||||
}
|
||||
& + div {padding-left: 0;}
|
||||
}
|
||||
.admin-tabs .btn-group {
|
||||
margin: 0;
|
||||
|
@ -64,113 +49,90 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-tabs--content {
|
||||
.panel {border-top-left-radius: 0;}
|
||||
.panel-heading {height: 60px;}
|
||||
.panel-title {
|
||||
font-size: 17px;
|
||||
font-weight: 400 !important;
|
||||
color: $g12-forge;
|
||||
padding: 0;
|
||||
}
|
||||
.panel-body {min-height: 300px;}
|
||||
.panel-heading + .panel-body {padding-top: 0;}
|
||||
}
|
||||
|
||||
/*
|
||||
Admin Table
|
||||
----------------------------------------------
|
||||
*/
|
||||
.admin-table {
|
||||
.multi-select-dropdown {
|
||||
width: 100%;
|
||||
min-width: 150px;
|
||||
}
|
||||
.admin-table--kill-button {
|
||||
width: 70px;
|
||||
}
|
||||
.admin-table--hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
.dropdown-toggle {
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: $g14-chromium;
|
||||
width: 100%;
|
||||
transition: none !important;
|
||||
.admin-table .dropdown-toggle {
|
||||
background-color: transparent;
|
||||
font-weight: 600;
|
||||
color: $g14-chromium;
|
||||
transition: none !important;
|
||||
|
||||
.caret {opacity: 0;}
|
||||
.multi-select-dropdown__label {left: 0;}
|
||||
}
|
||||
.open .dropdown-toggle .multi-select-dropdown__label {left: 9px;}
|
||||
tbody tr:hover {
|
||||
.admin-table--hidden {
|
||||
visibility: visible;
|
||||
}
|
||||
.dropdown-toggle {
|
||||
color: $g20-white !important;
|
||||
background-color: $c-pool;
|
||||
font-weight: 600;
|
||||
.caret {opacity: 0;}
|
||||
}
|
||||
.admin-table--multi-select-empty .dropdown-toggle {
|
||||
color: $g8-storm;
|
||||
}
|
||||
.admin-table tbody tr:hover .dropdown-toggle {
|
||||
color: $g20-white !important;
|
||||
background-color: $c-pool;
|
||||
|
||||
.caret {opacity: 1;}
|
||||
.multi-select-dropdown__label {left: 9px;}
|
||||
.caret {opacity: 1;}
|
||||
|
||||
&:hover {
|
||||
transition: background-color 0.25s ease;
|
||||
background-color: $c-laser;
|
||||
}
|
||||
}
|
||||
}
|
||||
.multi-select-dropdown.open .dropdown-toggle {
|
||||
color: $g20-white !important;
|
||||
background-color: $c-laser !important;
|
||||
font-weight: 600;
|
||||
.multi-select-dropdown__label {left: 9x !important;}
|
||||
&:hover {
|
||||
transition: background-color 0.25s ease;
|
||||
background-color: $c-laser;
|
||||
}
|
||||
}
|
||||
.admin-table--edit-row {
|
||||
background-color: $g4-onyx;
|
||||
table > tbody > tr > td.admin-table--left-offset,
|
||||
table > thead > tr > th.admin-table--left-offset {padding-left: 15px;}
|
||||
|
||||
table > tbody > tr.admin-table--edit-row,
|
||||
table > tbody > tr.admin-table--edit-row:hover,
|
||||
table.table-highlight > tbody > tr.admin-table--edit-row,
|
||||
table.table-highlight > tbody > tr.admin-table--edit-row:hover {
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
.admin-table--edit-cell {
|
||||
width: 100%;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
.admin-table--change-pw {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
> input {
|
||||
height: 30px;
|
||||
padding: 0 9px;
|
||||
flex-grow: 1;
|
||||
margin: 0 2px;
|
||||
min-width: 110px;
|
||||
|
||||
&:first-child {margin-left: 0;}
|
||||
&:last-child {margin-right: 0;}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-table--delete-cell {
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
|
||||
> input {
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
flex-grow: 1;
|
||||
.form-control {
|
||||
margin: 0 4px 0 0;
|
||||
min-width: 110px;
|
||||
font-size: 12px;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
}
|
||||
pre.admin-table--query {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.admin-table--delete-db {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& + .confirm-buttons .btn {
|
||||
display: block !important;
|
||||
> .form-control {
|
||||
flex: 1 0 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Database Manager
|
||||
----------------------------------------------
|
||||
*/
|
||||
.db-manager {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.db-manager-header--actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&:hover .db-manager-header--actions {
|
||||
display: block;
|
||||
}
|
||||
&:last-child {margin-bottom: 0;}
|
||||
.db-manager-header--actions {display: none;}
|
||||
&:hover .db-manager-header--actions {display: block;}
|
||||
}
|
||||
.db-manager-header {
|
||||
padding: 0 11px;
|
||||
|
@ -184,21 +146,18 @@
|
|||
h4 {
|
||||
margin: 0px;
|
||||
color: $c-potassium;
|
||||
font-size: 17px;
|
||||
font-size: 16px;
|
||||
font-family: $code-font;
|
||||
padding-left: 6px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.db-manager-header--edit {
|
||||
justify-content: flex-start;
|
||||
|
||||
.form-control {
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
margin: 0 4px 0 0;
|
||||
min-width: 110px;
|
||||
font-size: 12px;
|
||||
width: 50%;
|
||||
margin: 0 8px 0 0;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,20 +165,6 @@
|
|||
background-color: $g4-onyx;
|
||||
padding: 9px 11px;
|
||||
border-radius: 0 0 $radius-small $radius-small;
|
||||
}
|
||||
|
||||
|
||||
.admin-change-pw {
|
||||
float: right;
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.form-control {
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
margin: 0 4px 0 0;
|
||||
min-width: 110px;
|
||||
font-size: 12px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.table-highlight > tbody > tr:hover {background-color: $g5-pepper;}
|
||||
}
|
||||
|
|
|
@ -145,13 +145,13 @@ $dash-graph-options-arrow: 8px;
|
|||
height: $dash-graph-heading;
|
||||
line-height: $dash-graph-heading;
|
||||
width: calc(100% - 50px);
|
||||
margin-left: 16px;
|
||||
padding-left: 16px;
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
background-color 0.25s ease,
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
.dash-graph--name-edit {
|
||||
input.form-control.dash-graph--name-edit {
|
||||
margin-left: 8px;
|
||||
padding: 0 6px;
|
||||
width: calc(100% - 42px);
|
||||
|
@ -166,6 +166,7 @@ $dash-graph-options-arrow: 8px;
|
|||
z-index: 11;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
text-align: center;
|
||||
|
||||
> .btn {
|
||||
background-color: transparent !important;
|
||||
|
@ -197,6 +198,7 @@ $dash-graph-options-arrow: 8px;
|
|||
display: block;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 90px;
|
||||
visibility: hidden;
|
||||
transition-property: all;
|
||||
|
@ -208,6 +210,8 @@ $dash-graph-options-arrow: 8px;
|
|||
line-height: 28px;
|
||||
background-color: $g5-pepper;
|
||||
padding: 0 11px;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
color: $g15-platinum;
|
||||
opacity: 0;
|
||||
transition:
|
||||
|
|
|
@ -1,47 +1,230 @@
|
|||
/*
|
||||
Kapacitor Rule Builder
|
||||
----------------------------------------------
|
||||
---------------------------------------------------------------------------
|
||||
*/
|
||||
$kapacitor-page-padding: ($chronograf-page-header-height / 2);
|
||||
$kapacitor-page-gutter: 46px;
|
||||
$kapacitor-dot-size: 18px;
|
||||
$kapacitor-line-width: 3px;
|
||||
$metric-selector-height: 156px;
|
||||
$kap-padding-sm: 8px;
|
||||
$kap-padding-md: 13px;
|
||||
$kap-padding-lg: 24px;
|
||||
$kap-radius-lg: 5px;
|
||||
$kap-input-height: 30px;
|
||||
$rule-builder--accent-color: $c-rainforest;
|
||||
$rule-builder--left-gutter: 46px;
|
||||
$rule-builder--section-gap: ($chronograf-page-header-height / 2);
|
||||
$rule-builder--section-bg: $g3-castle;
|
||||
$rule-builder--section-border: $g2-kevlar;
|
||||
$rule-builder--dot: 18px;
|
||||
$rule-builder--accent-line-width: 3px;
|
||||
$rule-builder--accent-line-color: $g5-pepper;
|
||||
$rule-builder--font-size: 13px;
|
||||
$rule-builder--query-builder-height: 240px;
|
||||
|
||||
$kapacitor-graphic-color: $g3-castle;
|
||||
$kapacitor-divider-color: $g2-kevlar;
|
||||
$kapacitor-accent: $c-rainforest;
|
||||
$kap-line-color: $g5-pepper;
|
||||
$kap-dot-color: $c-rainforest;
|
||||
|
||||
$kapacitor-font-sm: 13px;
|
||||
$rule-builder--padding-sm: 8px;
|
||||
$rule-builder--padding-md: 13px;
|
||||
$rule-builder--padding-lg: 24px;
|
||||
$rule-builder--radius-lg: 5px;
|
||||
|
||||
.rule-builder {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.rule-builder p {
|
||||
width: auto;
|
||||
margin: 0 ($rule-builder--padding-sm - 2px);
|
||||
font-weight: 600;
|
||||
color: $g15-platinum;
|
||||
@include no-user-select();
|
||||
|
||||
&:first-child {margin-left: 0;}
|
||||
}
|
||||
|
||||
/*
|
||||
Generic Rule Section styles
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
.rule-section--heading,
|
||||
.rule-section--body {
|
||||
padding-left: $rule-builder--left-gutter;
|
||||
position: relative;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
// Vertical Line
|
||||
&:before {
|
||||
transform: translateX(-50%);
|
||||
width: $rule-builder--accent-line-width;
|
||||
height: 100%;
|
||||
background-color: $rule-builder--accent-line-color;
|
||||
top: 0;
|
||||
left: ($rule-builder--dot/2);
|
||||
}
|
||||
}
|
||||
.rule-section--heading {
|
||||
margin: 0;
|
||||
padding: $rule-builder--section-gap 0 $rule-builder--padding-md $rule-builder--left-gutter;
|
||||
font-size: $page-header-size;
|
||||
font-weight: $page-header-weight;
|
||||
color: $g12-forge;
|
||||
@include no-user-select();
|
||||
|
||||
// Dot
|
||||
&:after {
|
||||
transform: translateX(-50%);
|
||||
width: $rule-builder--dot;
|
||||
height: $rule-builder--dot;
|
||||
background-color: $rule-builder--accent-color;
|
||||
border: 6px solid $rule-builder--accent-line-color;
|
||||
border-radius: 50%;
|
||||
top: ($rule-builder--section-gap + 3px);
|
||||
left: ($rule-builder--dot / 2);
|
||||
}
|
||||
}
|
||||
// Override appearance of lines and dots for first section
|
||||
.rule-section:first-of-type {
|
||||
.rule-section--heading {
|
||||
padding-top: 0;
|
||||
}
|
||||
.rule-section--heading:before {
|
||||
top: 5px;
|
||||
height: calc(100% - 5px);
|
||||
}
|
||||
.rule-section--heading:after {
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
// Override appearance of lines and dots for last section
|
||||
.rule-section:last-of-type {
|
||||
.rule-section--heading:before {
|
||||
top: 0;
|
||||
height: ($rule-builder--section-gap + 3px + 3px);
|
||||
}
|
||||
.rule-section--body:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
// Generic re-usable classes for rule builder sections
|
||||
.rule-section--border-top {
|
||||
border-top: 2px solid $rule-builder--section-border;
|
||||
}
|
||||
.rule-section--border-bottom {
|
||||
border-bottom: 2px solid $rule-builder--section-border;
|
||||
}
|
||||
.rule-section--row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
background-color: $rule-builder--section-bg;
|
||||
padding: $rule-builder--padding-sm $rule-builder--padding-lg;
|
||||
}
|
||||
.rule-section--row-first {
|
||||
border-top-left-radius: $rule-builder--radius-lg;
|
||||
border-top-right-radius: $rule-builder--radius-lg;
|
||||
}
|
||||
.rule-section--row-last {
|
||||
border-bottom-left-radius: $rule-builder--radius-lg;
|
||||
border-bottom-right-radius: $rule-builder--radius-lg;
|
||||
}
|
||||
.rule-section--row .form-control + .form-control,
|
||||
.rule-section--row .dropdown + .form-control {
|
||||
margin-left: $rule-builder--padding-sm - 2px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Section 1 - Select a Time Series
|
||||
-----------------------------------------------------------------------------
|
||||
Overrides are scoped to the page class .rule-builder
|
||||
*/
|
||||
.rule-builder {
|
||||
/* Query Preview */
|
||||
pre {
|
||||
font-size: $rule-builder--font-size;
|
||||
background-color: $rule-builder--section-bg;
|
||||
border-radius: $rule-builder--radius-lg $rule-builder--radius-lg 0 0;
|
||||
margin: 0;
|
||||
padding: $rule-builder--padding-md $rule-builder--padding-lg;
|
||||
}
|
||||
pre code {
|
||||
line-height: ($rule-builder--font-size + 3px);
|
||||
white-space: pre-wrap;
|
||||
|
||||
&.metric-placeholder {
|
||||
color: $g8-storm;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
.query-builder {
|
||||
height: $rule-builder--query-builder-height;
|
||||
overflow: visible;
|
||||
}
|
||||
.query-builder--column {
|
||||
margin-right: 2px;
|
||||
&:last-child {margin-right: 0;}
|
||||
}
|
||||
.query-builder--column:nth-child(1) .query-builder--list {
|
||||
border-bottom-left-radius: $rule-builder--radius-lg;
|
||||
}
|
||||
.query-builder--column:nth-child(4) .query-builder--list,
|
||||
.query-builder--column:nth-child(4) .query-builder--list-empty {
|
||||
border-bottom-right-radius: $rule-builder--radius-lg;
|
||||
}
|
||||
.query-builder--heading {
|
||||
background-color: $rule-builder--section-bg;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.query-builder--list {
|
||||
@include custom-scrollbar($rule-builder--section-bg, $rule-builder--accent-color);
|
||||
}
|
||||
.group-by-tag.active {
|
||||
background-color: $c-rainforest !important;
|
||||
&:hover {background-color: $c-honeydew !important;}
|
||||
}
|
||||
.query-builder--list-item .query-builder--checkbox:after {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
.query-builder--filter {
|
||||
input.form-control {
|
||||
color: $c-rainforest !important;
|
||||
&:focus {
|
||||
color: $g20-white !important;
|
||||
box-shadow: 0 0 6px 0px $c-rainforest !important;
|
||||
border-color: $c-rainforest !important;
|
||||
}
|
||||
&:focus + span.icon {
|
||||
color: $c-rainforest !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sectiom 2 - Rule Conditions
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
.rule-builder--metric {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
background-color: $g5-pepper;
|
||||
color: $rule-builder--accent-color;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 0 9px;
|
||||
@include no-user-select();
|
||||
}
|
||||
.rule-builder--graph {
|
||||
margin-left: $kapacitor-page-gutter;
|
||||
width: calc(100% - #{$kapacitor-page-gutter});
|
||||
background-color: $kapacitor-graphic-color;
|
||||
border-radius: 0 0 $kap-radius-lg $kap-radius-lg;
|
||||
padding: 0 $kap-padding-sm;
|
||||
height: (300px + ($kap-padding-sm * 2));
|
||||
margin-left: $rule-builder--left-gutter;
|
||||
width: calc(100% - #{$rule-builder--left-gutter});
|
||||
background-color: $rule-builder--section-bg;
|
||||
border-radius: 0 0 $rule-builder--radius-lg $rule-builder--radius-lg;
|
||||
padding: 0 $rule-builder--padding-sm;
|
||||
height: (300px + ($rule-builder--padding-sm * 2));
|
||||
position: relative;
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: $kap-padding-sm;
|
||||
width: calc(100% - #{($kap-padding-sm * 2)});
|
||||
left: $rule-builder--padding-sm;
|
||||
width: calc(100% - #{($rule-builder--padding-sm * 2)});
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
|
@ -57,11 +240,11 @@ $kapacitor-font-sm: 13px;
|
|||
display: block;
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
width: $kapacitor-line-width;
|
||||
width: $rule-builder--accent-line-width;
|
||||
height: 100%;
|
||||
background-color: $kap-line-color;
|
||||
background-color: $rule-builder--accent-line-color;
|
||||
top: 0;
|
||||
left: (($kapacitor-dot-size / 2) - $kapacitor-page-gutter);
|
||||
left: (($rule-builder--dot / 2) - $rule-builder--left-gutter);
|
||||
}
|
||||
.container--dygraph-legend {
|
||||
background-color: $g5-pepper;
|
||||
|
@ -71,7 +254,7 @@ $kapacitor-font-sm: 13px;
|
|||
}
|
||||
}
|
||||
}
|
||||
.rule-preview--graph-empty {
|
||||
.rule-builder--graph-empty {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -96,378 +279,42 @@ $kapacitor-font-sm: 13px;
|
|||
}
|
||||
}
|
||||
|
||||
.rule-section-heading {
|
||||
margin: 0;
|
||||
padding: $kapacitor-page-padding 0 $kap-padding-md $kapacitor-page-gutter;
|
||||
font-size: $page-header-size;
|
||||
font-weight: $page-header-weight;
|
||||
color: $g12-forge;
|
||||
position: relative;
|
||||
@include no-user-select();
|
||||
/*
|
||||
Section 3 - Rule Message
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
// Vertical Line
|
||||
&:before {
|
||||
width: $kapacitor-line-width;
|
||||
height: 100%;
|
||||
background-color: $kap-line-color;
|
||||
top: 0;
|
||||
left: ($kapacitor-dot-size/2);
|
||||
}
|
||||
// Dot
|
||||
&:after {
|
||||
width: $kapacitor-dot-size;
|
||||
height: $kapacitor-dot-size;
|
||||
background-color: $kap-dot-color;
|
||||
border: 6px solid $kap-line-color;
|
||||
border-radius: 50%;
|
||||
top: ($kapacitor-page-padding + 3px);
|
||||
left: ($kapacitor-dot-size / 2);
|
||||
}
|
||||
}
|
||||
.rule-section-body {
|
||||
padding: 0 0 0 $kapacitor-page-gutter;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
|
||||
// Vertical Line
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
width: $kapacitor-line-width;
|
||||
height: 100%;
|
||||
background-color: $kap-line-color;
|
||||
top: 0;
|
||||
left: ($kapacitor-dot-size / 2);
|
||||
}
|
||||
}
|
||||
.kapacitor-rule-section {
|
||||
// Override appearance of lines and dots for first child
|
||||
&:first-of-type {
|
||||
.rule-section-heading:before {
|
||||
top: ($kapacitor-page-padding + 5px);
|
||||
height: calc(100% - #{$kapacitor-page-padding} - 5px);
|
||||
}
|
||||
}
|
||||
// Override appearance of lines and dots for last child
|
||||
&:last-of-type {
|
||||
.rule-section-heading:before {
|
||||
top: 0;
|
||||
height: ($kapacitor-page-padding + 3px + 3px);
|
||||
}
|
||||
.rule-section-body:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.kapacitor-metric-selector {
|
||||
/* Query Preview */
|
||||
pre {
|
||||
font-size: $kapacitor-font-sm;
|
||||
background-color: $kapacitor-graphic-color;
|
||||
color: $c-pool;
|
||||
border-radius: $kap-radius-lg $kap-radius-lg 0 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: $kap-padding-md $kap-padding-lg;
|
||||
}
|
||||
pre code {
|
||||
line-height: ($kapacitor-font-sm + 3px);
|
||||
white-space: pre-wrap;
|
||||
|
||||
&.kapacitor-metric-placeholder {
|
||||
color: $g8-storm;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
.query-builder {
|
||||
height: 240px;
|
||||
margin-top: 2px;
|
||||
overflow: visible;
|
||||
}
|
||||
.query-builder--column {
|
||||
margin-right: 2px;
|
||||
&:last-child {margin-right: 0;}
|
||||
}
|
||||
.query-builder--column:nth-child(1) .query-builder--list {
|
||||
border-bottom-left-radius: $kap-radius-lg;
|
||||
}
|
||||
.query-builder--column:nth-child(4) .query-builder--list,
|
||||
.query-builder--column:nth-child(4) .query-builder--list-empty {
|
||||
border-bottom-right-radius: $kap-radius-lg;
|
||||
}
|
||||
.query-builder--heading {
|
||||
background-color: $kapacitor-graphic-color;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.query-builder--list {
|
||||
@include custom-scrollbar($kapacitor-graphic-color, $kapacitor-accent);
|
||||
}
|
||||
.group-by-tag.active {
|
||||
background-color: $c-rainforest !important;
|
||||
&:hover {background-color: $c-honeydew !important;}
|
||||
}
|
||||
.query-builder--list-item .query-builder--checkbox:after {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
.query-builder--filter {
|
||||
input.form-control {
|
||||
color: $c-rainforest !important;
|
||||
&:focus {
|
||||
color: $g20-white !important;
|
||||
box-shadow: 0 0 6px 0px $c-rainforest !important;
|
||||
border-color: $c-rainforest !important;
|
||||
}
|
||||
&:focus + span.icon {
|
||||
color: $c-rainforest !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alert-text {
|
||||
border: 2px solid $g3-castle;
|
||||
background-color: $kapacitor-graphic-color;
|
||||
margin: 0;
|
||||
padding: $kap-padding-sm $kap-padding-lg;
|
||||
color: $kapacitor-accent;
|
||||
width: 100%;
|
||||
textarea.rule-builder--message {
|
||||
border-color: $rule-builder--section-bg;
|
||||
background-color: $rule-builder--section-bg;
|
||||
padding: $rule-builder--padding-sm ($rule-builder--padding-lg - 2px);
|
||||
height: 100px;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
font-family: Consolas, "Lucida Console", Monaco, monospace;
|
||||
font-weight: 600;
|
||||
font-size: $kapacitor-font-sm;
|
||||
line-height: 17px;
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
border-color 0.25s ease;
|
||||
border-radius: 0;
|
||||
@include custom-scrollbar($rule-builder--section-bg,$rule-builder--accent-color);
|
||||
|
||||
@include custom-scrollbar($kapacitor-graphic-color,$kapacitor-accent);
|
||||
&:hover {border-color: $g4-onyx;}
|
||||
&:focus {background-color: $rule-builder--section-bg;}
|
||||
}
|
||||
.rule-builder--message-template {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 ($rule-builder--padding-sm - 2px);
|
||||
margin: 2px;
|
||||
transition: color 0.25s ease;
|
||||
@include no-user-select();
|
||||
|
||||
&:hover {
|
||||
border-color: $g4-onyx;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
color: $g20-white;
|
||||
border-color: $kapacitor-accent;
|
||||
}
|
||||
&::-webkit-input-placeholder { color: $g9-mountain; }
|
||||
&::-moz-placeholder { color: $g9-mountain; }
|
||||
&:-ms-input-placeholder { color: $g9-mountain; }
|
||||
&:-moz-placeholder { color: $g9-mountain; }
|
||||
&::selection {
|
||||
background-color: $c-rainforest !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
&::-moz-selection {
|
||||
background-color: $c-rainforest !important;
|
||||
color: $g20-white !important;
|
||||
color: $rule-builder--accent-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-message--endpoint {
|
||||
width: auto;
|
||||
border-radius: $kap-radius-lg $kap-radius-lg 0 0;
|
||||
border-bottom: 2px solid $kapacitor-divider-color;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
p {
|
||||
margin-right: $kap-padding-sm !important;
|
||||
}
|
||||
}
|
||||
.alert-message--formatting {
|
||||
padding: ($kap-padding-sm - 2px) $kap-padding-lg;
|
||||
background-color: $kapacitor-graphic-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
border-top: 2px solid $kapacitor-divider-color;
|
||||
border-radius: 0 0 $kap-radius-lg $kap-radius-lg;
|
||||
|
||||
> p {
|
||||
margin: 0 ($kap-padding-sm - 2px) 0 0;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
color: $g15-platinum;
|
||||
@include no-user-select();
|
||||
}
|
||||
> code {
|
||||
background-color: $g2-kevlar;
|
||||
height: $kap-input-height;
|
||||
line-height: $kap-input-height;
|
||||
padding: 0 ($kap-padding-sm - 2px);
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
margin: 2px;
|
||||
color: $c-pool;
|
||||
font-size: ($kapacitor-font-sm - 2px);
|
||||
font-weight: 600;
|
||||
transition: color 0.25s ease;
|
||||
@include no-user-select();
|
||||
|
||||
&:hover {
|
||||
color: $c-rainforest;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert-message--config {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
border-bottom: 2px solid $kapacitor-divider-color;
|
||||
|
||||
& > p {
|
||||
margin-right: ($kap-padding-sm - 2px) !important;
|
||||
}
|
||||
}
|
||||
.alert-message--email-body {
|
||||
border-bottom: 2px solid $kapacitor-divider-color;
|
||||
}
|
||||
|
||||
.rule-section--item {
|
||||
background-color: $kapacitor-graphic-color;
|
||||
padding: $kap-padding-sm $kap-padding-lg;
|
||||
overflow: visible;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
color: $g15-platinum;
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
&.top {
|
||||
border-top-left-radius: $kap-radius-lg;
|
||||
border-top-right-radius: $kap-radius-lg;
|
||||
}
|
||||
&.bottom {
|
||||
border-bottom-left-radius: $kap-radius-lg;
|
||||
border-bottom-right-radius: $kap-radius-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.kapacitor-values-tabs,
|
||||
.kapacitor-alert-message,
|
||||
.value-selector {
|
||||
background-color: $kapacitor-graphic-color;
|
||||
padding: ($kap-padding-sm - 2px) $kap-padding-lg;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
display: inline-block;
|
||||
margin: 2px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> p {
|
||||
@include no-user-select();
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
color: $g15-platinum;
|
||||
margin-left: ($kap-padding-sm / 2);
|
||||
margin-right: ($kap-padding-sm / 2);
|
||||
}
|
||||
> span {
|
||||
@include no-user-select();
|
||||
color: $kapacitor-accent;
|
||||
height: $kap-input-height;
|
||||
line-height: $kap-input-height;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
background-color: $g5-pepper;
|
||||
margin: 2px;
|
||||
padding: 0 $kap-padding-sm;
|
||||
font-weight: 700;
|
||||
font-size: $kapacitor-font-sm;
|
||||
|
||||
&:hover {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.kapacitor-values-tabs,
|
||||
.kapacitor-alert-message, {
|
||||
border-radius: $kap-radius-lg $kap-radius-lg 0 0;
|
||||
border-bottom: 2px solid $kapacitor-divider-color;
|
||||
|
||||
.tab-group {
|
||||
padding: 0;
|
||||
|
||||
> .btn.tab {
|
||||
padding: 0 $kap-padding-md;
|
||||
height: $kap-input-height;
|
||||
line-height: ($kap-input-height - 4px);
|
||||
font-size: $kapacitor-font-sm;
|
||||
font-weight: 700;
|
||||
background-color: $kapacitor-graphic-color;
|
||||
border-color: $g5-pepper;
|
||||
color: $g11-sidewalk;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
color: $g20-white;
|
||||
}
|
||||
&.active {
|
||||
background-color: $g5-pepper;
|
||||
color: $kapacitor-accent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.rule-builder .form-control--green {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
input.size-486 {width: 486px;}
|
||||
input.size-384 {width: 384px;}
|
||||
input.size-256 {width: 256px;}
|
||||
input.size-176 {width: 176px;}
|
||||
input.size-166 {width: 166px;}
|
||||
input.size-136 {width: 136px;}
|
||||
input.size-106 {width: 106px;}
|
||||
input.size-66 {width: 66px;}
|
||||
input.size-49 {width: 49px;}
|
||||
|
||||
.dropdown.size-486 .dropdown-toggle {width: 486px;}
|
||||
.dropdown.size-384 .dropdown-toggle {width: 384px;}
|
||||
.dropdown.size-256 .dropdown-toggle {width: 256px;}
|
||||
.dropdown.size-176 .dropdown-toggle {width: 176px;}
|
||||
.dropdown.size-166 .dropdown-toggle {width: 166px;}
|
||||
.dropdown.size-136 .dropdown-toggle {width: 136px;}
|
||||
.dropdown.size-106 .dropdown-toggle {width: 106px;}
|
||||
.dropdown.size-66 .dropdown-toggle {width: 66px;}
|
||||
.dropdown.size-49 .dropdown-toggle {width: 49px;}
|
||||
|
||||
/*
|
||||
Color coding for alerts in Alert History table
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
.alert-level-ok {
|
||||
&, &:hover {color: $c-rainforest !important;}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ $overlay-z: 100;
|
|||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 $overlay-controls-height;
|
||||
width: calc(100% - #{($explorer-page-padding * 2)});
|
||||
left: $explorer-page-padding;
|
||||
width: calc(100% - #{($page-wrapper-padding * 2)});
|
||||
left: $page-wrapper-padding;
|
||||
border: 0;
|
||||
background-color: $g2-kevlar;
|
||||
border-radius: $radius $radius 0 0;
|
||||
|
@ -54,13 +54,16 @@ $overlay-z: 100;
|
|||
margin: 0 0 0 5px;
|
||||
}
|
||||
p {
|
||||
width: auto;
|
||||
font-weight: 600;
|
||||
color: $g13-mist;
|
||||
margin: 0;
|
||||
margin: 0 6px 0 0;
|
||||
@include no-user-select;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.overlay--graph-name {
|
||||
width: auto;
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
|
@ -78,28 +81,6 @@ $overlay-z: 100;
|
|||
.overlay-controls .confirm-buttons {
|
||||
margin-left: 32px;
|
||||
}
|
||||
.overlay-controls .confirm-buttons .btn {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 9px;
|
||||
|
||||
& > span.icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.overlay-controls .toggle {
|
||||
|
||||
.toggle-btn {
|
||||
background-color: $overlay-controls-bg;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
&.active {
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Graph editing in Dashboards is a little smaller so the dash can be seen in the background */
|
||||
.overlay-technology .graph {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,611 +0,0 @@
|
|||
/*
|
||||
Dark Theme Styles
|
||||
----------------------------------------------
|
||||
This stylesheet has overrides for the theme, so you are
|
||||
going to see al ot of !important =(
|
||||
|
||||
This is design debt. One day the theme will not have to be overrided
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Dark Panel Styles
|
||||
----------------------------------------------
|
||||
*/
|
||||
.row:only-child .panel,
|
||||
.row:last-child .panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.panel hr {
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
.panel-title,
|
||||
.panel-title a {
|
||||
@include no-user-select();
|
||||
}
|
||||
.panel-minimal {
|
||||
border: 0;
|
||||
|
||||
.panel-title {
|
||||
color: $g12-forge !important;
|
||||
font-size: 19px;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
.panel-body {
|
||||
padding: 30px;
|
||||
background-color: $g3-castle;
|
||||
border: 0;
|
||||
color: $g13-mist;
|
||||
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
> *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.panel.panel-info {
|
||||
background-color: $g3-castle;
|
||||
border: 0;
|
||||
|
||||
.panel-body,
|
||||
.panel-heading {
|
||||
background-color: transparent;
|
||||
}
|
||||
.panel-body {
|
||||
padding: 0px 30px 30px 30px;
|
||||
}
|
||||
.panel-heading {
|
||||
padding: 0 30px;
|
||||
height: 60px;
|
||||
border: 0px;
|
||||
.panel-title { color: $g14-chromium;}
|
||||
}
|
||||
}
|
||||
.panel .panel-body table {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table thead th {
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Dark Buttons
|
||||
----------------------------------------------
|
||||
*/
|
||||
.btn {
|
||||
border: 0;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease,
|
||||
box-shadow 0.25s ease;
|
||||
}
|
||||
.btn.btn-sm {
|
||||
font-size: 13px;
|
||||
line-height: 30px !important;
|
||||
height: 30px !important;
|
||||
padding: 0 9px !important;
|
||||
}
|
||||
a.btn.btn-sm > span.icon,
|
||||
div.btn.btn-sm > span.icon,
|
||||
button.btn.btn-sm > span.icon {
|
||||
font-size: 16px;
|
||||
margin: 0 6px 0 0 ;
|
||||
}
|
||||
.btn.btn-xs > .icon {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.btn.disabled,
|
||||
.btn[disabled="true"],
|
||||
.btn[disabled] {
|
||||
&.btn-success,
|
||||
&.btn-success:hover,
|
||||
&.btn-success:focus,
|
||||
&.btn-success:active,
|
||||
&.btn-success:hover:active,
|
||||
&.btn-success:hover:focus,
|
||||
&.btn-success:focus:active,
|
||||
&.btn-success:focus:active:hover {
|
||||
border-color: $c-emerald;
|
||||
background-color: $c-emerald;
|
||||
color: $g2-kevlar !important;
|
||||
}
|
||||
&.btn-primary,
|
||||
&.btn-primary:hover,
|
||||
&.btn-primary:focus,
|
||||
&.btn-primary:active,
|
||||
&.btn-primary:hover:active,
|
||||
&.btn-primary:hover:focus,
|
||||
&.btn-primary:focus:active,
|
||||
&.btn-primary:focus:active:hover {
|
||||
border-color: $c-sapphire;
|
||||
background-color: $c-sapphire;
|
||||
color: $g2-kevlar !important;
|
||||
}
|
||||
&.btn-info,
|
||||
&.btn-info:hover,
|
||||
&.btn-info:focus,
|
||||
&.btn-info:active,
|
||||
&.btn-info:hover:active,
|
||||
&.btn-info:hover:focus,
|
||||
&.btn-info:focus:active,
|
||||
&.btn-info:focus:active:hover {
|
||||
border-color: $g5-pepper;
|
||||
background-color: $g5-pepper;
|
||||
color: $g8-storm !important;
|
||||
}
|
||||
&.btn-danger,
|
||||
&.btn-danger:hover,
|
||||
&.btn-danger:focus,
|
||||
&.btn-danger:active,
|
||||
&.btn-danger:hover:active,
|
||||
&.btn-danger:hover:focus,
|
||||
&.btn-danger:focus:active,
|
||||
&.btn-danger:focus:active:hover {
|
||||
border-color: $c-ruby;
|
||||
background-color: $c-ruby;
|
||||
color: $g2-kevlar !important;
|
||||
}
|
||||
&.btn-warning,
|
||||
&.btn-warning:hover,
|
||||
&.btn-warning:focus,
|
||||
&.btn-warning:active,
|
||||
&.btn-warning:hover:active,
|
||||
&.btn-warning:hover:focus,
|
||||
&.btn-warning:focus:active,
|
||||
&.btn-warning:focus:active:hover {
|
||||
border-color: $c-star;
|
||||
background-color: $c-star;
|
||||
color: $g2-kevlar !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Active state for buttons */
|
||||
.btn-info.active {
|
||||
&, &:hover, &:active, &:focus {
|
||||
background-color: $g7-graphite;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Dark Inputs
|
||||
----------------------------------------------
|
||||
*/
|
||||
.form-group {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
.form-group label {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 13px;
|
||||
@include no-user-select();
|
||||
}
|
||||
.form-control {
|
||||
padding: 0 13px;
|
||||
background-color: $g2-kevlar !important;
|
||||
border-color: $g5-pepper !important;
|
||||
color: $g15-platinum !important;
|
||||
|
||||
&:hover {
|
||||
border-color: $g6-smoke !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $c-pool !important;
|
||||
box-shadow: 0 0 6px 0px $c-pool !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
|
||||
&--green:focus {
|
||||
border-color: $c-rainforest !important;
|
||||
box-shadow: 0 0 6px 0px $c-rainforest !important;
|
||||
}
|
||||
}
|
||||
.form-helper {
|
||||
margin: 4px 0 !important;
|
||||
font-weight: 400 !important;
|
||||
font-style: italic;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
.form-group-submit {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* Placeholder Text */
|
||||
.form-control,
|
||||
textarea,
|
||||
input {
|
||||
&::-webkit-input-placeholder { color: $g9-mountain; font-weight: 500 !important; }
|
||||
&::-moz-placeholder { color: $g9-mountain; font-weight: 500 !important; }
|
||||
&:-ms-input-placeholder { color: $g9-mountain; font-weight: 500 !important; }
|
||||
&:-moz-placeholder { color: $g9-mountain; font-weight: 500 !important; }
|
||||
}
|
||||
|
||||
/* Text Selection Styling */
|
||||
::selection {
|
||||
background-color: $c-pool !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
::-moz-selection {
|
||||
background-color: $c-pool !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
|
||||
/* Green (Kapacitor) themed inputs */
|
||||
.form-control--green {
|
||||
color: $c-rainforest !important;
|
||||
&:focus {color: $g20-white !important;}
|
||||
|
||||
&::selection {
|
||||
background-color: $c-rainforest !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
&::-moz-selection {
|
||||
background-color: $c-rainforest !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Dark Code Samples
|
||||
----------------------------------------------
|
||||
*/
|
||||
code, pre {
|
||||
font-family: 'RobotoMono', monospace !important;
|
||||
}
|
||||
code {
|
||||
display: inline-block;
|
||||
background-color: $g2-kevlar;
|
||||
color: $c-comet;
|
||||
border: 0 !important;
|
||||
border-radius: 2px;
|
||||
padding: 2.5px 5px 2.5px 4px;
|
||||
margin: 0 1px 0 2px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Dark Modals
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.modal-backdrop {
|
||||
background: $c-pool;
|
||||
background: -moz-linear-gradient(-45deg, $c-pool 0%, $c-comet 100%);
|
||||
background: -webkit-linear-gradient(-45deg, $c-pool 0%,$c-comet 100%);
|
||||
background: linear-gradient(135deg, $c-pool 0%,$c-comet 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$c-pool', endColorstr='$c-comet',GradientType=1 );
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
.modal-header,
|
||||
.modal-body,
|
||||
.modal-footer {
|
||||
background-color: transparent;
|
||||
color: $g13-mist;
|
||||
padding-left: ($sidebar-width / 2);
|
||||
padding-right: ($sidebar-width / 2);
|
||||
}
|
||||
.modal-body {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.modal-title {
|
||||
color: $g18-cloud !important;
|
||||
}
|
||||
.modal-header .close {
|
||||
transition: none;
|
||||
outline: none;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
display: block;
|
||||
content: '';
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
background-color: $g7-graphite;
|
||||
transition: background-color 0.25s ease;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
&:before { transform: translate(-50%,-50%) rotate(45deg); }
|
||||
&:after { transform: translate(-50%,-50%) rotate(-45deg); }
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $g13-mist;
|
||||
}
|
||||
}
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Dark Toggles
|
||||
----------------------------------------------
|
||||
*/
|
||||
$toggle-height-sm: 30px;
|
||||
$toggle-font-sm: 13px;
|
||||
$toggle-padding-sm: 9px;
|
||||
|
||||
$toggle-height-md: 30px;
|
||||
$toggle-font-md: 13px;
|
||||
$toggle-padding-md: 9px;
|
||||
|
||||
$toggle-border: 2px;
|
||||
|
||||
.toggle {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
padding: $toggle-border;
|
||||
border-radius: 3px;
|
||||
background-color: $g5-pepper;
|
||||
font-size: 0;
|
||||
white-space: nowrap;
|
||||
@include no-user-select();
|
||||
}
|
||||
.toggle-btn {
|
||||
border: 0;
|
||||
border-radius: 1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: auto;
|
||||
background-color: $g3-castle;
|
||||
color: $g11-sidewalk;
|
||||
transition:
|
||||
background-color 0.25s,
|
||||
color 0.25s;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g14-chromium;
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
&.active {
|
||||
background-color: $g5-pepper;
|
||||
color: $g18-cloud;
|
||||
}
|
||||
}
|
||||
.toggle-sm {
|
||||
height: $toggle-height-sm;
|
||||
|
||||
.toggle-btn {
|
||||
height: ($toggle-height-sm - ($toggle-border * 2));
|
||||
line-height: ($toggle-height-sm - ($toggle-border * 2));
|
||||
padding: 0 16px;
|
||||
font-size: $toggle-font-sm;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Static Form Controls
|
||||
----------------------------------------------
|
||||
*/
|
||||
$form-static-checkbox-size: 16px;
|
||||
.form-control-static {
|
||||
border: 2px solid $g5-pepper;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
padding: 7px 12px;
|
||||
position: relative;
|
||||
|
||||
input[type="checkbox"] {
|
||||
position: relative;
|
||||
left: -9999px;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
|
||||
// Faux Checkbox
|
||||
& + label {
|
||||
font-size: 14px !important;
|
||||
line-height: 16px;
|
||||
color: $g11-sidewalk;
|
||||
font-weight: 500;
|
||||
transition: color 0.25s ease;
|
||||
margin: 0;
|
||||
padding: 0 0 0 (12px + $form-static-checkbox-size) !important;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-selct: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 12px;
|
||||
transform: translateY(-50%);
|
||||
width: $form-static-checkbox-size;
|
||||
height: $form-static-checkbox-size;
|
||||
background-color: $g2-kevlar;
|
||||
border: 2px solid $g5-pepper;
|
||||
border-radius: 3px;
|
||||
z-index: 2;
|
||||
transition:
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: (12px + ($form-static-checkbox-size / 2));
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-pool;
|
||||
z-index: 3;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
|
||||
&:before {
|
||||
border-color: $g6-smoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Faux Checkbox (Checked)
|
||||
&:checked + label {
|
||||
color: $g20-white;
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control-static .radio {
|
||||
margin: 0;
|
||||
|
||||
input[type="radio"] {
|
||||
position: relative;
|
||||
left: -9999px;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
|
||||
// Faux Checkbox
|
||||
& + label {
|
||||
font-size: 14px !important;
|
||||
line-height: 16px;
|
||||
color: $g11-sidewalk;
|
||||
font-weight: 500;
|
||||
transition: color 0.25s ease;
|
||||
margin: 0;
|
||||
padding: 0 0 0 (12px + $form-static-checkbox-size) !important;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-selct: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: $form-static-checkbox-size;
|
||||
height: $form-static-checkbox-size;
|
||||
background-color: $g2-kevlar;
|
||||
border: 2px solid $g5-pepper;
|
||||
border-radius: 50%;
|
||||
z-index: 2;
|
||||
transition:
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: ($form-static-checkbox-size / 2);
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-pool;
|
||||
z-index: 3;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
|
||||
&:before {
|
||||
border-color: $g6-smoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Faux Checkbox (Checked)
|
||||
&:checked + label {
|
||||
color: $g20-white;
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark-checkbox {
|
||||
input {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
visibility: hidden;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
width: $form-static-checkbox-size;
|
||||
height: $form-static-checkbox-size;
|
||||
background-color: $g1-raven;
|
||||
border-radius: $radius-small;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
transition: background-color 0.25s ease;
|
||||
}
|
||||
label:hover {
|
||||
cursor: pointer;
|
||||
background-color: $g2-kevlar;
|
||||
}
|
||||
label:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: $c-pool;
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
transform 0.25s ease;
|
||||
}
|
||||
input:checked + label:after {
|
||||
opacity: 1;
|
||||
transform: translate(-50%,-50%) scale(1,1);
|
||||
}
|
||||
}
|
||||
|
||||
br {
|
||||
@include no-user-select();
|
||||
}
|
|
@ -29,21 +29,6 @@
|
|||
color: $g8-storm;
|
||||
}
|
||||
}
|
||||
.btn-block.dropdown-toggle {
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 18px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
& + .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.modal {
|
||||
form {
|
||||
padding: 0;
|
||||
|
@ -59,10 +44,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.form-group label,
|
||||
.form-group label:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
Generic Empty State
|
||||
|
@ -71,67 +52,25 @@
|
|||
.generic-empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $g12-forge;
|
||||
padding: 20px 0;
|
||||
|
||||
h4, h5 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-bottom: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
// Tabs for switching between queries
|
||||
.qeditor--tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 9px 0 9px;
|
||||
background-color: $query-editor-tab-inactive;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&-heading {
|
||||
flex-basis: 100%;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
color: $g9-mountain;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
}
|
||||
.qeditor--tab {
|
||||
text-align: center;
|
||||
background-color: $query-editor-tab-inactive;
|
||||
color: $g13-mist;
|
||||
height: 28px;
|
||||
padding: 0 9px;
|
||||
line-height: 28px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-radius: $radius-small $radius-small 0 0;
|
||||
margin-right: 2px;
|
||||
@include no-user-select();
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
background-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
}
|
||||
&.active {
|
||||
background-color: $query-editor-tab-active;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Loading Dots
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.loading-dots {
|
||||
position: absolute;
|
||||
transform: translate(0,0);
|
||||
|
@ -154,7 +93,6 @@
|
|||
div:nth-child(3) {left: 100%; animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Custom Tabs
|
||||
----------------------------------------------
|
||||
|
@ -206,7 +144,9 @@
|
|||
@include no-user-select();
|
||||
}
|
||||
|
||||
|
||||
br {
|
||||
@include no-user-select();
|
||||
}
|
||||
|
||||
.select-source-page {
|
||||
position: absolute;
|
||||
|
|
Loading…
Reference in New Issue