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
|
### Bug Fixes
|
||||||
1. [#1530](https://github.com/influxdata/chronograf/pull/1530): Update query config field ordering to always match input query
|
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
|
### 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. [#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. [#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. [#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]
|
## 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
|
### UI Improvements
|
||||||
1. [#1451](https://github.com/influxdata/chronograf/pull/1451): Refactor scrollbars to support non-webkit browsers
|
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. [#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. [#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
|
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.2 [MIT](https://github.com/niklasvh/base64-arraybuffer)
|
||||||
* base64-arraybuffer 0.1.5 [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)
|
* 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)
|
* batch 0.5.3 [MIT](https://github.com/visionmedia/batch)
|
||||||
* bcrypt-pbkdf 1.0.0 [BSD-4-Clause]((none))
|
* bcrypt-pbkdf 1.0.0 [BSD-4-Clause]((none))
|
||||||
* benchmark 1.0.0 [MIT](https://github.com/bestiejs/benchmark.js)
|
* 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,
|
Href: task.Link.Href,
|
||||||
HrefOutput: c.HrefOutput(kapaID),
|
HrefOutput: c.HrefOutput(kapaID),
|
||||||
TICKScript: script,
|
TICKScript: script,
|
||||||
Rule: rule,
|
Rule: c.Reverse(kapaID, script),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,22 @@ func (c *Client) All(ctx context.Context) (map[string]chronograf.AlertRule, erro
|
||||||
return alerts, nil
|
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
|
// Get returns a single alert in kapacitor
|
||||||
func (c *Client) Get(ctx context.Context, id string) (chronograf.AlertRule, error) {
|
func (c *Client) Get(ctx context.Context, id string) (chronograf.AlertRule, error) {
|
||||||
kapa, err := c.kapaClient(c.URL, c.Username, c.Password)
|
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)
|
script := chronograf.TICKScript(task.TICKscript)
|
||||||
rule, err := Reverse(script)
|
return c.Reverse(task.ID, script), nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update changes the tickscript of a given id.
|
// 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,
|
Href: task.Link.Href,
|
||||||
HrefOutput: c.HrefOutput(task.ID),
|
HrefOutput: c.HrefOutput(task.ID),
|
||||||
TICKScript: script,
|
TICKScript: script,
|
||||||
Rule: rule,
|
Rule: c.Reverse(task.ID, script),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,15 @@ type MockKapa struct {
|
||||||
ResTasks []client.Task
|
ResTasks []client.Task
|
||||||
Error error
|
Error error
|
||||||
|
|
||||||
client.CreateTaskOptions
|
*client.CreateTaskOptions
|
||||||
client.Link
|
client.Link
|
||||||
*client.TaskOptions
|
*client.TaskOptions
|
||||||
*client.ListTasksOptions
|
*client.ListTasksOptions
|
||||||
client.UpdateTaskOptions
|
*client.UpdateTaskOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockKapa) CreateTask(opt client.CreateTaskOptions) (client.Task, error) {
|
func (m *MockKapa) CreateTask(opt client.CreateTaskOptions) (client.Task, error) {
|
||||||
m.CreateTaskOptions = opt
|
m.CreateTaskOptions = &opt
|
||||||
return m.ResTask, m.Error
|
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) {
|
func (m *MockKapa) UpdateTask(link client.Link, opt client.UpdateTaskOptions) (client.Task, error) {
|
||||||
m.Link = link
|
m.Link = link
|
||||||
m.UpdateTaskOptions = opt
|
if m.UpdateTaskOptions == nil {
|
||||||
|
m.UpdateTaskOptions = &opt
|
||||||
|
}
|
||||||
return m.ResTask, m.Error
|
return m.ResTask, m.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +51,13 @@ func (m *MockKapa) DeleteTask(link client.Link) error {
|
||||||
return m.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) {
|
func TestClient_AllStatus(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
URL string
|
URL string
|
||||||
|
@ -160,9 +169,6 @@ func TestClient_AllStatus(t *testing.T) {
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("Client.AllStatus() = %v, want %v", 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) {
|
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||||
t.Errorf("Client.AllStatus() = listTasksOptions %v, want %v", 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) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("Client.All() = %#v, want %#v", 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) {
|
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||||
t.Errorf("Client.All() = listTasksOptions %v, want %v", 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) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("Client.Get() =\n%#v\nwant\n%#v", 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) {
|
if !reflect.DeepEqual(kapa.ListTasksOptions, tt.listTasksOptions) {
|
||||||
t.Errorf("Client.Get() = listTasksOptions %v, want %v", 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
|
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
|
// KapacitorRulesPut proxies PATCH to kapacitor
|
||||||
func (h *Service) KapacitorRulesPut(w http.ResponseWriter, r *http.Request) {
|
func (h *Service) KapacitorRulesPut(w http.ResponseWriter, r *http.Request) {
|
||||||
id, err := paramID("kid", r)
|
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)
|
Error(w, http.StatusInternalServerError, err.Error(), h.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
res := newAlertResponse(task.Rule, task.TICKScript, task.Href, task.HrefOutput, "enabled", srv.SrcID, srv.ID)
|
||||||
res := newAlertResponse(req, task.TICKScript, task.Href, task.HrefOutput, "enabled", srv.SrcID, srv.ID)
|
|
||||||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
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 {
|
import {
|
||||||
chooseTrigger,
|
chooseTrigger,
|
||||||
|
addEvery,
|
||||||
|
removeEvery,
|
||||||
updateRuleValues,
|
updateRuleValues,
|
||||||
updateDetails,
|
updateDetails,
|
||||||
updateMessage,
|
updateMessage,
|
||||||
|
@ -38,6 +40,23 @@ describe('Kapacitor.Reducers.rules', () => {
|
||||||
expect(newState[ruleID].values).to.equal(defaultRuleConfigs.threshold)
|
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', () => {
|
it('can update the values', () => {
|
||||||
const ruleID = 1
|
const ruleID = 1
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
@ -50,11 +69,17 @@ describe('Kapacitor.Reducers.rules', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newDeadmanValues = {duration: '5m'}
|
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)
|
expect(newState[ruleID].values).to.equal(newDeadmanValues)
|
||||||
|
|
||||||
const newRelativeValues = {func: 'max', change: 'change'}
|
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].trigger).to.equal('relative')
|
||||||
expect(finalState[ruleID].values).to.equal(newRelativeValues)
|
expect(finalState[ruleID].values).to.equal(newRelativeValues)
|
||||||
})
|
})
|
||||||
|
@ -110,7 +135,10 @@ describe('Kapacitor.Reducers.rules', () => {
|
||||||
.services('a b c')
|
.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')`
|
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])
|
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)
|
expect(newState[ruleID].status).to.equal(status)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -96,7 +96,7 @@ const AdminTabs = ({
|
||||||
<TabList customClass="col-md-2 admin-tabs">
|
<TabList customClass="col-md-2 admin-tabs">
|
||||||
{tabs.map((t, i) => <Tab key={tabs[i].type}>{tabs[i].type}</Tab>)}
|
{tabs.map((t, i) => <Tab key={tabs[i].type}>{tabs[i].type}</Tab>)}
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels customClass="col-md-10">
|
<TabPanels customClass="col-md-10 admin-tabs--content">
|
||||||
{tabs.map((t, i) => (
|
{tabs.map((t, i) => (
|
||||||
<TabPanel key={tabs[i].type}>{t.component}</TabPanel>
|
<TabPanel key={tabs[i].type}>{t.component}</TabPanel>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -48,17 +48,17 @@ class ChangePassRow extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {user} = this.props
|
const {user, buttonSize} = this.props
|
||||||
|
|
||||||
if (this.state.showForm) {
|
if (this.state.showForm) {
|
||||||
return (
|
return (
|
||||||
<div className="admin-change-pw">
|
<div className="admin-table--change-pw">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control input-xs"
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
value={user.password || ''}
|
value={user.password || ''}
|
||||||
placeholder="Password"
|
placeholder="New password"
|
||||||
onChange={this.handleEdit(user)}
|
onChange={this.handleEdit(user)}
|
||||||
onKeyPress={this.handleKeyPress(user)}
|
onKeyPress={this.handleKeyPress(user)}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
@ -67,28 +67,29 @@ class ChangePassRow extends Component {
|
||||||
onConfirm={this.handleSubmit}
|
onConfirm={this.handleSubmit}
|
||||||
item={user}
|
item={user}
|
||||||
onCancel={this.handleCancel}
|
onCancel={this.handleCancel}
|
||||||
|
buttonSize={buttonSize}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<div className="admin-table--change-pw">
|
||||||
className="btn btn-xs btn-info admin-table--hidden"
|
<a href="#" onClick={this.showForm}>
|
||||||
onClick={this.showForm}
|
Change
|
||||||
>
|
</a>
|
||||||
Change Password
|
</div>
|
||||||
</button>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {shape, func} = PropTypes
|
const {func, shape, string} = PropTypes
|
||||||
|
|
||||||
ChangePassRow.propTypes = {
|
ChangePassRow.propTypes = {
|
||||||
user: shape().isRequired,
|
user: shape().isRequired,
|
||||||
onApply: func.isRequired,
|
onApply: func.isRequired,
|
||||||
onEdit: func.isRequired,
|
onEdit: func.isRequired,
|
||||||
|
buttonSize: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OnClickOutside(ChangePassRow)
|
export default OnClickOutside(ChangePassRow)
|
||||||
|
|
|
@ -25,7 +25,7 @@ const DatabaseManager = ({
|
||||||
onDeleteRetentionPolicy,
|
onDeleteRetentionPolicy,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="panel panel-info">
|
<div className="panel panel-default">
|
||||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||||
<h2 className="panel-title">
|
<h2 className="panel-title">
|
||||||
{databases.length === 1
|
{databases.length === 1
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
import onClickOutside from 'react-onclickoutside'
|
||||||
|
|
||||||
import {formatRPDuration} from 'utils/formatting'
|
import {formatRPDuration} from 'utils/formatting'
|
||||||
import YesNoButtons from 'src/shared/components/YesNoButtons'
|
import YesNoButtons from 'src/shared/components/YesNoButtons'
|
||||||
import onClickOutside from 'react-onclickoutside'
|
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
class DatabaseRow extends Component {
|
class DatabaseRow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -46,51 +48,55 @@ class DatabaseRow extends Component {
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{isNew
|
{isNew
|
||||||
? <div className="admin-table--edit-cell">
|
? <input
|
||||||
<input
|
className="form-control input-xs"
|
||||||
className="form-control"
|
type="text"
|
||||||
type="text"
|
defaultValue={name}
|
||||||
defaultValue={name}
|
placeholder="Name this RP"
|
||||||
placeholder="Name this RP"
|
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
ref={r => (this.name = r)}
|
||||||
ref={r => (this.name = r)}
|
autoFocus={true}
|
||||||
autoFocus={true}
|
spellCheck={false}
|
||||||
/>
|
autoComplete={false}
|
||||||
</div>
|
/>
|
||||||
: <div className="admin-table--edit-cell">
|
: name}
|
||||||
{name}
|
|
||||||
</div>}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{width: `${DATABASE_TABLE.colDuration}px`}}>
|
||||||
<div className="admin-table--edit-cell">
|
<input
|
||||||
<input
|
className="form-control input-xs"
|
||||||
className="form-control"
|
name="name"
|
||||||
name="name"
|
type="text"
|
||||||
type="text"
|
defaultValue={formattedDuration}
|
||||||
defaultValue={formattedDuration}
|
placeholder="How long should Data last"
|
||||||
placeholder="How long should Data last"
|
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
ref={r => (this.duration = r)}
|
||||||
ref={r => (this.duration = r)}
|
autoFocus={!isNew}
|
||||||
autoFocus={!isNew}
|
spellCheck={false}
|
||||||
/>
|
autoComplete={false}
|
||||||
</div>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td style={isRFDisplayed ? {} : {display: 'none'}}>
|
{isRFDisplayed
|
||||||
<div className="admin-table--edit-cell">
|
? <td style={{width: `${DATABASE_TABLE.colReplication}px`}}>
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control input-xs"
|
||||||
name="name"
|
name="name"
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
defaultValue={replication || 1}
|
defaultValue={replication || 1}
|
||||||
placeholder="# of Nodes"
|
placeholder="# of Nodes"
|
||||||
onKeyDown={e => this.handleKeyDown(e, database)}
|
onKeyDown={e => this.handleKeyDown(e, database)}
|
||||||
ref={r => (this.replication = r)}
|
ref={r => (this.replication = r)}
|
||||||
/>
|
spellCheck={false}
|
||||||
</div>
|
autoComplete={false}
|
||||||
</td>
|
/>
|
||||||
<td className="text-right">
|
</td>
|
||||||
|
: null}
|
||||||
|
<td
|
||||||
|
className="text-right"
|
||||||
|
style={{width: `${DATABASE_TABLE.colDelete}px`}}
|
||||||
|
>
|
||||||
<YesNoButtons
|
<YesNoButtons
|
||||||
|
buttonSize="btn-xs"
|
||||||
onConfirm={isNew ? this.handleCreate : this.handleUpdate}
|
onConfirm={isNew ? this.handleCreate : this.handleUpdate}
|
||||||
onCancel={
|
onCancel={
|
||||||
isNew
|
isNew
|
||||||
|
@ -106,24 +112,37 @@ class DatabaseRow extends Component {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{name}
|
{`${name} `}
|
||||||
{' '}
|
|
||||||
{isDefault
|
{isDefault
|
||||||
? <span className="default-source-label">default</span>
|
? <span className="default-source-label">default</span>
|
||||||
: null}
|
: null}
|
||||||
</td>
|
</td>
|
||||||
<td onClick={this.handleStartEdit}>{formattedDuration}</td>
|
<td
|
||||||
|
onClick={this.handleStartEdit}
|
||||||
|
style={{width: `${DATABASE_TABLE.colDuration}px`}}
|
||||||
|
>
|
||||||
|
{formattedDuration}
|
||||||
|
</td>
|
||||||
{isRFDisplayed
|
{isRFDisplayed
|
||||||
? <td onClick={this.handleStartEdit}>{replication}</td>
|
? <td
|
||||||
|
onClick={this.handleStartEdit}
|
||||||
|
style={{width: `${DATABASE_TABLE.colReplication}px`}}
|
||||||
|
>
|
||||||
|
{replication}
|
||||||
|
</td>
|
||||||
: null}
|
: null}
|
||||||
<td className="text-right">
|
<td
|
||||||
|
className="text-right"
|
||||||
|
style={{width: `${DATABASE_TABLE.colDelete}px`}}
|
||||||
|
>
|
||||||
{isDeleting
|
{isDeleting
|
||||||
? <YesNoButtons
|
? <YesNoButtons
|
||||||
onConfirm={() => onDelete(database, retentionPolicy)}
|
onConfirm={() => onDelete(database, retentionPolicy)}
|
||||||
onCancel={this.handleEndDelete}
|
onCancel={this.handleEndDelete}
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
: <button
|
: <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'}}
|
style={isDeletable ? {} : {visibility: 'hidden'}}
|
||||||
onClick={this.handleStartDelete}
|
onClick={this.handleStartDelete}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import DatabaseRow from 'src/admin/components/DatabaseRow'
|
import DatabaseRow from 'src/admin/components/DatabaseRow'
|
||||||
import DatabaseTableHeader from 'src/admin/components/DatabaseTableHeader'
|
import DatabaseTableHeader from 'src/admin/components/DatabaseTableHeader'
|
||||||
|
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
const {func, shape, bool} = PropTypes
|
const {func, shape, bool} = PropTypes
|
||||||
|
|
||||||
|
@ -26,7 +28,11 @@ const DatabaseTable = ({
|
||||||
onDeleteRetentionPolicy,
|
onDeleteRetentionPolicy,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="db-manager">
|
<div
|
||||||
|
className={classnames('db-manager', {
|
||||||
|
'db-manager--edit': database.isEditing,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<DatabaseTableHeader
|
<DatabaseTableHeader
|
||||||
database={database}
|
database={database}
|
||||||
notify={notify}
|
notify={notify}
|
||||||
|
@ -43,13 +49,21 @@ const DatabaseTable = ({
|
||||||
isAddRPDisabled={!!database.retentionPolicies.some(rp => rp.isNew)}
|
isAddRPDisabled={!!database.retentionPolicies.some(rp => rp.isNew)}
|
||||||
/>
|
/>
|
||||||
<div className="db-manager-table">
|
<div className="db-manager-table">
|
||||||
<table className="table v-center admin-table">
|
<table className="table v-center table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Retention Policy</th>
|
<th>
|
||||||
<th>Duration</th>
|
Retention Policy
|
||||||
{isRFDisplayed ? <th>Replication Factor</th> : null}
|
</th>
|
||||||
<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>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -52,12 +52,6 @@ const Header = ({
|
||||||
onAddRetentionPolicy,
|
onAddRetentionPolicy,
|
||||||
onDatabaseDeleteConfirm,
|
onDatabaseDeleteConfirm,
|
||||||
}) => {
|
}) => {
|
||||||
const confirmStyle = {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons = (
|
const buttons = (
|
||||||
<div className="text-right db-manager-header--actions">
|
<div className="text-right db-manager-header--actions">
|
||||||
<button
|
<button
|
||||||
|
@ -87,23 +81,24 @@ const Header = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteConfirmation = (
|
const deleteConfirmation = (
|
||||||
<div style={confirmStyle}>
|
<div className="admin-table--delete-db">
|
||||||
<div className="admin-table--delete-cell">
|
<input
|
||||||
<input
|
className="form-control input-xs"
|
||||||
className="form-control"
|
name="name"
|
||||||
name="name"
|
type="text"
|
||||||
type="text"
|
value={database.deleteCode || ''}
|
||||||
value={database.deleteCode || ''}
|
placeholder={`DELETE ${database.name}`}
|
||||||
placeholder={`DELETE ${database.name}`}
|
onChange={e => onDatabaseDeleteConfirm(database, e)}
|
||||||
onChange={e => onDatabaseDeleteConfirm(database, e)}
|
onKeyDown={e => onDatabaseDeleteConfirm(database, e)}
|
||||||
onKeyDown={e => onDatabaseDeleteConfirm(database, e)}
|
autoFocus={true}
|
||||||
autoFocus={true}
|
autoComplete={false}
|
||||||
/>
|
spellCheck={false}
|
||||||
</div>
|
/>
|
||||||
<ConfirmButtons
|
<ConfirmButtons
|
||||||
item={database}
|
item={database}
|
||||||
onConfirm={onConfirm}
|
onConfirm={onConfirm}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -119,14 +114,16 @@ const Header = ({
|
||||||
const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
|
const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
|
||||||
<div className="db-manager-header db-manager-header--edit">
|
<div className="db-manager-header db-manager-header--edit">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control input-sm"
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
value={database.name}
|
value={database.name}
|
||||||
placeholder="Name this database"
|
placeholder="Name this Database"
|
||||||
onChange={e => onEdit(database, {name: e.target.value})}
|
onChange={e => onEdit(database, {name: e.target.value})}
|
||||||
onKeyDown={e => onKeyDown(e, database)}
|
onKeyDown={e => onKeyDown(e, database)}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete={false}
|
||||||
/>
|
/>
|
||||||
<ConfirmButtons item={database} onConfirm={onConfirm} onCancel={onCancel} />
|
<ConfirmButtons item={database} onConfirm={onConfirm} onCancel={onCancel} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,7 +31,7 @@ class FilterBar extends Component {
|
||||||
<div className="users__search-widget input-group admin__search-widget">
|
<div className="users__search-widget input-group admin__search-widget">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control input-sm"
|
||||||
placeholder={`Filter ${placeholderText}...`}
|
placeholder={`Filter ${placeholderText}...`}
|
||||||
value={this.state.filterText}
|
value={this.state.filterText}
|
||||||
onChange={this.handleText}
|
onChange={this.handleText}
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import QueryRow from 'src/admin/components/QueryRow'
|
import QueryRow from 'src/admin/components/QueryRow'
|
||||||
|
import {QUERIES_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
const QueriesTable = ({queries, onKillQuery}) => (
|
const QueriesTable = ({queries, onKillQuery}) => (
|
||||||
<div>
|
<div>
|
||||||
<div className="panel panel-minimal">
|
<div className="panel panel-default">
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<table className="table v-center admin-table">
|
<table className="table v-center admin-table table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Database</th>
|
<th style={{width: `${QUERIES_TABLE.colDatabase}px`}}>
|
||||||
|
Database
|
||||||
|
</th>
|
||||||
<th>Query</th>
|
<th>Query</th>
|
||||||
<th>Running</th>
|
<th style={{width: `${QUERIES_TABLE.colRunning}px`}}>
|
||||||
<th />
|
Running
|
||||||
|
</th>
|
||||||
|
<th style={{width: `${QUERIES_TABLE.colKillQuery}px`}} />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
|
||||||
import ConfirmButtons from 'src/shared/components/ConfirmButtons'
|
import ConfirmButtons from 'src/shared/components/ConfirmButtons'
|
||||||
|
import {QUERIES_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
class QueryRow extends Component {
|
class QueryRow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -32,17 +33,31 @@ class QueryRow extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{database}</td>
|
<td
|
||||||
|
style={{width: `${QUERIES_TABLE.colDatabase}px`}}
|
||||||
|
className="monotype"
|
||||||
|
>
|
||||||
|
{database}
|
||||||
|
</td>
|
||||||
<td><code>{query}</code></td>
|
<td><code>{query}</code></td>
|
||||||
<td>{duration}</td>
|
<td
|
||||||
<td className="admin-table--kill-button text-right">
|
style={{width: `${QUERIES_TABLE.colRunning}px`}}
|
||||||
|
className="monotype"
|
||||||
|
>
|
||||||
|
{duration}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style={{width: `${QUERIES_TABLE.colKillQuery}px`}}
|
||||||
|
className="text-right"
|
||||||
|
>
|
||||||
{this.state.confirmingKill
|
{this.state.confirmingKill
|
||||||
? <ConfirmButtons
|
? <ConfirmButtons
|
||||||
onConfirm={this.handleFinishHim}
|
onConfirm={this.handleFinishHim}
|
||||||
onCancel={this.handleShowMercy}
|
onCancel={this.handleShowMercy}
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
: <button
|
: <button
|
||||||
className="btn btn-xs btn-danger admin-table--hidden"
|
className="btn btn-xs btn-danger table--show-on-row-hover"
|
||||||
onClick={this.handleInitiateKill}
|
onClick={this.handleInitiateKill}
|
||||||
>
|
>
|
||||||
Kill
|
Kill
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
|
||||||
|
import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
class RoleEditingRow extends Component {
|
class RoleEditingRow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
@ -25,19 +27,19 @@ class RoleEditingRow extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {role} = this.props
|
const {role} = this.props
|
||||||
return (
|
return (
|
||||||
<td>
|
<td style={{width: `${ROLES_TABLE.colName}px`}}>
|
||||||
<div className="admin-table--edit-cell">
|
<input
|
||||||
<input
|
className="form-control input-xs"
|
||||||
className="form-control"
|
name="name"
|
||||||
name="name"
|
type="text"
|
||||||
type="text"
|
value={role.name || ''}
|
||||||
value={role.name || ''}
|
placeholder="Role name"
|
||||||
placeholder="role name"
|
onChange={this.handleEdit(role)}
|
||||||
onChange={this.handleEdit(role)}
|
onKeyPress={this.handleKeyPress(role)}
|
||||||
onKeyPress={this.handleKeyPress(role)}
|
autoFocus={true}
|
||||||
autoFocus={true}
|
spellCheck={false}
|
||||||
/>
|
autoComplete={false}
|
||||||
</div>
|
/>
|
||||||
</td>
|
</td>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import RoleEditingRow from 'src/admin/components/RoleEditingRow'
|
import RoleEditingRow from 'src/admin/components/RoleEditingRow'
|
||||||
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||||
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
||||||
|
import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
const RoleRow = ({
|
const RoleRow = ({
|
||||||
role: {name, permissions, users},
|
role: {name, permissions, users},
|
||||||
|
@ -40,10 +42,18 @@ const RoleRow = ({
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
isNew={isNew}
|
isNew={isNew}
|
||||||
/>
|
/>
|
||||||
<td />
|
<td className="admin-table--left-offset">--</td>
|
||||||
<td />
|
<td className="admin-table--left-offset">--</td>
|
||||||
<td className="text-right" style={{width: '85px'}}>
|
<td
|
||||||
<ConfirmButtons item={role} onConfirm={onSave} onCancel={onCancel} />
|
className="text-right"
|
||||||
|
style={{width: `${ROLES_TABLE.colDelete}px`}}
|
||||||
|
>
|
||||||
|
<ConfirmButtons
|
||||||
|
item={role}
|
||||||
|
onConfirm={onSave}
|
||||||
|
onCancel={onCancel}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
|
@ -51,7 +61,7 @@ const RoleRow = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{name}</td>
|
<td style={{width: `${ROLES_TABLE.colName}px`}}>{name}</td>
|
||||||
<td>
|
<td>
|
||||||
{allPermissions && allPermissions.length
|
{allPermissions && allPermissions.length
|
||||||
? <MultiSelectDropdown
|
? <MultiSelectDropdown
|
||||||
|
@ -59,6 +69,14 @@ const RoleRow = ({
|
||||||
selectedItems={perms}
|
selectedItems={perms}
|
||||||
label={perms.length ? '' : 'Select Permissions'}
|
label={perms.length ? '' : 'Select Permissions'}
|
||||||
onApply={handleUpdatePermissions}
|
onApply={handleUpdatePermissions}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
buttonColor="btn-primary"
|
||||||
|
customClass={classnames(
|
||||||
|
`dropdown-${ROLES_TABLE.colPermissions}`,
|
||||||
|
{
|
||||||
|
'admin-table--multi-select-empty': !permissions.length,
|
||||||
|
}
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</td>
|
</td>
|
||||||
|
@ -69,10 +87,22 @@ const RoleRow = ({
|
||||||
selectedItems={users === undefined ? [] : users.map(u => u.name)}
|
selectedItems={users === undefined ? [] : users.map(u => u.name)}
|
||||||
label={users && users.length ? '' : 'Select Users'}
|
label={users && users.length ? '' : 'Select Users'}
|
||||||
onApply={handleUpdateUsers}
|
onApply={handleUpdateUsers}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
buttonColor="btn-primary"
|
||||||
|
customClass={classnames(
|
||||||
|
`dropdown-${ROLES_TABLE.colUsers}`,
|
||||||
|
{
|
||||||
|
'admin-table--multi-select-empty': !users.length,
|
||||||
|
}
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</td>
|
</td>
|
||||||
<DeleteConfirmTableCell onDelete={onDelete} item={role} />
|
<DeleteConfirmTableCell
|
||||||
|
onDelete={onDelete}
|
||||||
|
item={role}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
/>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ const RolesTable = ({
|
||||||
onUpdateRoleUsers,
|
onUpdateRoleUsers,
|
||||||
onUpdateRolePermissions,
|
onUpdateRolePermissions,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="panel panel-info">
|
<div className="panel panel-default">
|
||||||
<FilterBar
|
<FilterBar
|
||||||
type="roles"
|
type="roles"
|
||||||
onFilter={onFilter}
|
onFilter={onFilter}
|
||||||
|
@ -25,12 +25,12 @@ const RolesTable = ({
|
||||||
onClickCreate={onClickCreate}
|
onClickCreate={onClickCreate}
|
||||||
/>
|
/>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<table className="table v-center admin-table">
|
<table className="table v-center admin-table table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Permissions</th>
|
<th className="admin-table--left-offset">Permissions</th>
|
||||||
<th>Users</th>
|
<th className="admin-table--left-offset">Users</th>
|
||||||
<th />
|
<th />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</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 React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import _ from 'lodash'
|
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 MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
|
||||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||||
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
|
||||||
import ChangePassRow from 'src/admin/components/ChangePassRow'
|
import ChangePassRow from 'src/admin/components/ChangePassRow'
|
||||||
|
import {USERS_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
const UserRow = ({
|
const UserRow = ({
|
||||||
user: {name, roles, permissions, password},
|
user: {name, roles, permissions, password},
|
||||||
|
@ -42,16 +45,25 @@ const UserRow = ({
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
<tr className="admin-table--edit-row">
|
<tr className="admin-table--edit-row">
|
||||||
<UserEditingRow
|
<UserEditName user={user} onEdit={onEdit} onSave={onSave} />
|
||||||
|
<UserNewPassword
|
||||||
user={user}
|
user={user}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
isNew={isNew}
|
isNew={isNew}
|
||||||
/>
|
/>
|
||||||
{hasRoles ? <td /> : null}
|
{hasRoles ? <td className="admin-table--left-offset">--</td> : null}
|
||||||
<td />
|
<td className="admin-table--left-offset">--</td>
|
||||||
<td className="text-right" style={{width: '85px'}}>
|
<td
|
||||||
<ConfirmButtons item={user} onConfirm={onSave} onCancel={onCancel} />
|
className="text-right"
|
||||||
|
style={{width: `${USERS_TABLE.colDelete}px`}}
|
||||||
|
>
|
||||||
|
<ConfirmButtons
|
||||||
|
item={user}
|
||||||
|
onConfirm={onSave}
|
||||||
|
onCancel={onCancel}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
|
@ -59,7 +71,15 @@ const UserRow = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<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
|
{hasRoles
|
||||||
? <td>
|
? <td>
|
||||||
<MultiSelectDropdown
|
<MultiSelectDropdown
|
||||||
|
@ -71,6 +91,14 @@ const UserRow = ({
|
||||||
}
|
}
|
||||||
label={roles && roles.length ? '' : 'Select Roles'}
|
label={roles && roles.length ? '' : 'Select Roles'}
|
||||||
onApply={handleUpdateRoles}
|
onApply={handleUpdateRoles}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
buttonColor="btn-primary"
|
||||||
|
customClass={classnames(
|
||||||
|
`dropdown-${USERS_TABLE.colRoles}`,
|
||||||
|
{
|
||||||
|
'admin-table--multi-select-empty': !roles.length,
|
||||||
|
}
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
: null}
|
: null}
|
||||||
|
@ -83,17 +111,22 @@ const UserRow = ({
|
||||||
permissions && permissions.length ? '' : 'Select Permissions'
|
permissions && permissions.length ? '' : 'Select Permissions'
|
||||||
}
|
}
|
||||||
onApply={handleUpdatePermissions}
|
onApply={handleUpdatePermissions}
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
buttonColor="btn-primary"
|
||||||
|
customClass={classnames(
|
||||||
|
`dropdown-${USERS_TABLE.colPermissions}`,
|
||||||
|
{
|
||||||
|
'admin-table--multi-select-empty': !permissions.length,
|
||||||
|
}
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right" style={{width: '300px'}}>
|
<DeleteConfirmTableCell
|
||||||
<ChangePassRow
|
onDelete={onDelete}
|
||||||
onEdit={onEdit}
|
item={user}
|
||||||
onApply={handleUpdatePassword}
|
buttonSize="btn-xs"
|
||||||
user={user}
|
/>
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<DeleteConfirmTableCell onDelete={onDelete} item={user} />
|
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ const UsersTable = ({
|
||||||
onUpdateRoles,
|
onUpdateRoles,
|
||||||
onUpdatePassword,
|
onUpdatePassword,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="panel panel-info">
|
<div className="panel panel-default">
|
||||||
<FilterBar
|
<FilterBar
|
||||||
type="users"
|
type="users"
|
||||||
onFilter={onFilter}
|
onFilter={onFilter}
|
||||||
|
@ -28,13 +28,13 @@ const UsersTable = ({
|
||||||
onClickCreate={onClickCreate}
|
onClickCreate={onClickCreate}
|
||||||
/>
|
/>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<table className="table v-center admin-table">
|
<table className="table v-center admin-table table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
{hasRoles && <th>Roles</th>}
|
<th>Password</th>
|
||||||
<th>Permissions</th>
|
{hasRoles && <th className="admin-table--left-offset">Roles</th>}
|
||||||
<th />
|
<th className="admin-table--left-offset">Permissions</th>
|
||||||
<th />
|
<th />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</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>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
{this.props.alerts.length
|
{this.props.alerts.length
|
||||||
? <table className="table v-center">
|
? <table className="table v-center table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
|
@ -149,9 +149,9 @@ const AlertsTable = React.createClass({
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
: <div className="generic-empty-state">
|
: <div className="generic-empty-state">
|
||||||
<h4 className="no-user-select">
|
<h5 className="no-user-select">
|
||||||
Alerts appear here when you have Rules
|
Alerts appear here when you have Rules
|
||||||
</h4>
|
</h5>
|
||||||
<br />
|
<br />
|
||||||
<Link
|
<Link
|
||||||
to={`/sources/${id}/alert-rules/new`}
|
to={`/sources/${id}/alert-rules/new`}
|
||||||
|
@ -201,7 +201,7 @@ const SearchBar = React.createClass({
|
||||||
value={this.state.searchTerm}
|
value={this.state.searchTerm}
|
||||||
/>
|
/>
|
||||||
<div className="input-group-addon">
|
<div className="input-group-addon">
|
||||||
<span className="icon search" aria-hidden="true" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -35,10 +35,10 @@ const DashboardHeader = ({
|
||||||
type="button"
|
type="button"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
>
|
>
|
||||||
<span className="button-text">{buttonText}</span>
|
<span>{buttonText}</span>
|
||||||
<span className="caret" />
|
<span className="caret" />
|
||||||
</button>
|
</button>
|
||||||
<ul className="dropdown-menu" aria-labelledby="dropdownMenu1">
|
<ul className="dropdown-menu">
|
||||||
{children}
|
{children}
|
||||||
</ul>
|
</ul>
|
||||||
</div>}
|
</div>}
|
||||||
|
@ -55,7 +55,7 @@ const DashboardHeader = ({
|
||||||
: null}
|
: null}
|
||||||
{dashboard
|
{dashboard
|
||||||
? <button
|
? <button
|
||||||
className="btn btn-info btn-sm"
|
className="btn btn-default btn-sm"
|
||||||
onClick={onEditDashboard}
|
onClick={onEditDashboard}
|
||||||
>
|
>
|
||||||
<span className="icon pencil" />
|
<span className="icon pencil" />
|
||||||
|
@ -64,7 +64,7 @@ const DashboardHeader = ({
|
||||||
: null}
|
: null}
|
||||||
{dashboard
|
{dashboard
|
||||||
? <div
|
? <div
|
||||||
className={classnames('btn btn-info btn-sm', {
|
className={classnames('btn btn-default btn-sm', {
|
||||||
active: showTemplateControlBar,
|
active: showTemplateControlBar,
|
||||||
})}
|
})}
|
||||||
onClick={onToggleTempVarControls}
|
onClick={onToggleTempVarControls}
|
||||||
|
@ -82,7 +82,7 @@ const DashboardHeader = ({
|
||||||
selected={timeRange}
|
selected={timeRange}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="btn btn-info btn-sm"
|
className="btn btn-default btn-sm btn-square"
|
||||||
onClick={handleClickPresentationButton}
|
onClick={handleClickPresentationButton}
|
||||||
>
|
>
|
||||||
<span className="icon expand-a" style={{margin: 0}} />
|
<span className="icon expand-a" style={{margin: 0}} />
|
||||||
|
|
|
@ -36,15 +36,20 @@ class DashboardEditHeader extends Component {
|
||||||
return (
|
return (
|
||||||
<div className="page-header full-width">
|
<div className="page-header full-width">
|
||||||
<div className="page-header__container">
|
<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
|
<input
|
||||||
className="page-header--editing"
|
className="page-header--editing"
|
||||||
name="name"
|
name="name"
|
||||||
autoFocus={true}
|
|
||||||
value={name}
|
value={name}
|
||||||
placeholder="Name this Dashboard"
|
placeholder="Name this Dashboard"
|
||||||
onChange={e => this.handleChange(e.target.value)}
|
onChange={e => this.handleChange(e.target.value)}
|
||||||
onKeyUp={this.handleKeyUp}
|
onKeyUp={this.handleKeyUp}
|
||||||
|
autoFocus={true}
|
||||||
|
spellCheck={false}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -12,11 +12,11 @@ const OverlayControls = props => {
|
||||||
<h3 className="overlay--graph-name">Cell Editor</h3>
|
<h3 className="overlay--graph-name">Cell Editor</h3>
|
||||||
<div className="overlay-controls--right">
|
<div className="overlay-controls--right">
|
||||||
<p>Visualization Type:</p>
|
<p>Visualization Type:</p>
|
||||||
<ul className="toggle toggle-sm">
|
<ul className="nav nav-tablist nav-tablist-sm">
|
||||||
{graphTypes.map(graphType => (
|
{graphTypes.map(graphType => (
|
||||||
<li
|
<li
|
||||||
key={graphType.type}
|
key={graphType.type}
|
||||||
className={classnames('toggle-btn', {
|
className={classnames({
|
||||||
active: graphType.type === selectedGraphType,
|
active: graphType.type === selectedGraphType,
|
||||||
})}
|
})}
|
||||||
onClick={() => onSelectGraphType(graphType.type)}
|
onClick={() => onSelectGraphType(graphType.type)}
|
||||||
|
|
|
@ -28,6 +28,7 @@ const TemplateControlBar = ({
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={items}
|
items={items}
|
||||||
buttonSize="btn-xs"
|
buttonSize="btn-xs"
|
||||||
|
menuClass="dropdown-astronaut"
|
||||||
useAutoComplete={true}
|
useAutoComplete={true}
|
||||||
selected={selectedText || '(No values)'}
|
selected={selectedText || '(No values)'}
|
||||||
onChoose={item =>
|
onChoose={item =>
|
||||||
|
|
|
@ -84,7 +84,7 @@ const RowButtons = ({
|
||||||
<div className="tvm-actions">
|
<div className="tvm-actions">
|
||||||
<DeleteConfirmButtons onDelete={() => onDelete(id)} />
|
<DeleteConfirmButtons onDelete={() => onDelete(id)} />
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-info btn-edit"
|
className="btn btn-sm btn-info btn-edit btn-square"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
// prevent subsequent 'onSubmit' that is caused by an unknown source,
|
// prevent subsequent 'onSubmit' that is caused by an unknown source,
|
||||||
|
|
|
@ -304,10 +304,7 @@ class DashboardPage extends Component {
|
||||||
{dashboards
|
{dashboards
|
||||||
? dashboards.map((d, i) => (
|
? dashboards.map((d, i) => (
|
||||||
<li className="dropdown-item" key={i}>
|
<li className="dropdown-item" key={i}>
|
||||||
<Link
|
<Link to={`/sources/${sourceID}/dashboards/${d.id}`}>
|
||||||
to={`/sources/${sourceID}/dashboards/${d.id}`}
|
|
||||||
className="role-option"
|
|
||||||
>
|
|
||||||
{d.name}
|
{d.name}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -89,7 +89,7 @@ const DashboardsPage = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
{dashboards && dashboards.length
|
{dashboards && dashboards.length
|
||||||
? <table className="table v-center admin-table">
|
? <table className="table v-center admin-table table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
@ -109,6 +109,7 @@ const DashboardsPage = React.createClass({
|
||||||
<DeleteConfirmTableCell
|
<DeleteConfirmTableCell
|
||||||
onDelete={this.handleDeleteDashboard}
|
onDelete={this.handleDeleteDashboard}
|
||||||
item={dashboard}
|
item={dashboard}
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
import FieldListItem from 'src/data_explorer/components/FieldListItem'
|
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 FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
|
||||||
import {showFieldKeys} from 'shared/apis/metaQuery'
|
import {showFieldKeys} from 'shared/apis/metaQuery'
|
||||||
|
@ -77,7 +78,7 @@ const FieldList = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {query} = this.props
|
const {query, isKapacitorRule} = this.props
|
||||||
const hasAggregates = query.fields.some(f => f.funcs && f.funcs.length)
|
const hasAggregates = query.fields.some(f => f.funcs && f.funcs.length)
|
||||||
const hasGroupByTime = query.groupBy.time
|
const hasGroupByTime = query.groupBy.time
|
||||||
|
|
||||||
|
@ -90,6 +91,7 @@ const FieldList = React.createClass({
|
||||||
isOpen={!hasGroupByTime}
|
isOpen={!hasGroupByTime}
|
||||||
selected={query.groupBy.time}
|
selected={query.groupBy.time}
|
||||||
onChooseGroupByTime={this.handleGroupByTime}
|
onChooseGroupByTime={this.handleGroupByTime}
|
||||||
|
isInRuleBuilder={isKapacitorRule}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,7 +56,9 @@ const FieldListItem = React.createClass({
|
||||||
if (isKapacitorRule) {
|
if (isKapacitorRule) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
className={classnames('query-builder--list-item', {
|
||||||
|
active: isSelected,
|
||||||
|
})}
|
||||||
key={fieldFunc}
|
key={fieldFunc}
|
||||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||||
>
|
>
|
||||||
|
@ -66,14 +68,16 @@ const FieldListItem = React.createClass({
|
||||||
</span>
|
</span>
|
||||||
{isSelected
|
{isSelected
|
||||||
? <Dropdown
|
? <Dropdown
|
||||||
|
className="dropdown-110"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
|
buttonSize="btn-xs"
|
||||||
items={items}
|
items={items}
|
||||||
onChoose={this.handleApplyFunctions}
|
onChoose={this.handleApplyFunctions}
|
||||||
selected={
|
selected={
|
||||||
fieldFunc.funcs.length ? fieldFunc.funcs[0] : 'Function'
|
fieldFunc.funcs.length ? fieldFunc.funcs[0] : 'Function'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
: null
|
: null}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -81,7 +85,9 @@ const FieldListItem = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div key={fieldFunc}>
|
<div key={fieldFunc}>
|
||||||
<div
|
<div
|
||||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
className={classnames('query-builder--list-item', {
|
||||||
|
active: isSelected,
|
||||||
|
})}
|
||||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
@ -89,13 +95,17 @@ const FieldListItem = React.createClass({
|
||||||
{fieldText}
|
{fieldText}
|
||||||
</span>
|
</span>
|
||||||
{isSelected
|
{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
|
Functions
|
||||||
</div>
|
</div>
|
||||||
: null
|
: null}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
{(isSelected && isOpen)
|
{isSelected && isOpen
|
||||||
? <FunctionSelector
|
? <FunctionSelector
|
||||||
onApply={this.handleApplyFunctions}
|
onApply={this.handleApplyFunctions}
|
||||||
selectedItems={fieldFunc.funcs || []}
|
selectedItems={fieldFunc.funcs || []}
|
||||||
|
|
|
@ -1,49 +1,33 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
|
||||||
|
|
||||||
import groupByTimeOptions from 'hson!../data/groupByTimes.hson'
|
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({
|
const GroupByTimeDropdown = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
isOpen: bool,
|
|
||||||
selected: string,
|
selected: string,
|
||||||
onChooseGroupByTime: func.isRequired,
|
onChooseGroupByTime: func.isRequired,
|
||||||
|
isInRuleBuilder: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {isOpen, selected, onChooseGroupByTime} = this.props
|
const {selected, onChooseGroupByTime, isInRuleBuilder} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Dropdown
|
||||||
className={classnames('dropdown group-by-time-dropdown', {
|
className="dropdown-130"
|
||||||
open: isOpen,
|
menuClass={isInRuleBuilder ? 'dropdown-malachite' : null}
|
||||||
})}
|
buttonColor={isInRuleBuilder ? 'btn-default' : 'btn-info'}
|
||||||
>
|
items={groupByTimeOptions.map(groupBy => ({
|
||||||
<div
|
...groupBy,
|
||||||
className="btn btn-sm btn-info dropdown-toggle"
|
text: groupBy.menuOption,
|
||||||
data-toggle="dropdown"
|
}))}
|
||||||
>
|
onChoose={onChooseGroupByTime}
|
||||||
<span>Group by {selected || 'time'}</span>
|
selected={selected ? `Group by ${selected}` : 'Group by Time'}
|
||||||
<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>
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -96,6 +96,8 @@ const MeasurementList = React.createClass({
|
||||||
value={this.state.filterText}
|
value={this.state.filterText}
|
||||||
onChange={this.handleFilterText}
|
onChange={this.handleFilterText}
|
||||||
onKeyUp={this.handleEscape}
|
onKeyUp={this.handleEscape}
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete={false}
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -254,7 +254,8 @@ class QueryEditor extends Component {
|
||||||
items={QUERY_TEMPLATES}
|
items={QUERY_TEMPLATES}
|
||||||
selected={'Query Templates'}
|
selected={'Query Templates'}
|
||||||
onChoose={this.handleChooseTemplate}
|
onChoose={this.handleChooseTemplate}
|
||||||
className="query-editor--templates"
|
className="dropdown-140 query-editor--templates"
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -270,7 +271,8 @@ class QueryEditor extends Component {
|
||||||
items={QUERY_TEMPLATES}
|
items={QUERY_TEMPLATES}
|
||||||
selected={'Query Templates'}
|
selected={'Query Templates'}
|
||||||
onChoose={this.handleChooseTemplate}
|
onChoose={this.handleChooseTemplate}
|
||||||
className="query-editor--templates"
|
className="dropdown-140 query-editor--templates"
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -300,7 +302,8 @@ class QueryEditor extends Component {
|
||||||
items={QUERY_TEMPLATES}
|
items={QUERY_TEMPLATES}
|
||||||
selected={'Query Templates'}
|
selected={'Query Templates'}
|
||||||
onChoose={this.handleChooseTemplate}
|
onChoose={this.handleChooseTemplate}
|
||||||
className="query-editor--templates"
|
className="dropdown-140 query-editor--templates"
|
||||||
|
buttonSize="btn-xs"
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -66,6 +66,8 @@ const TagListItem = React.createClass({
|
||||||
value={this.state.filterText}
|
value={this.state.filterText}
|
||||||
onChange={this.handleFilterText}
|
onChange={this.handleFilterText}
|
||||||
onKeyUp={this.handleEscape}
|
onKeyUp={this.handleEscape}
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete={false}
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,7 +109,7 @@ const TagListItem = React.createClass({
|
||||||
{tagItemLabel}
|
{tagItemLabel}
|
||||||
</span>
|
</span>
|
||||||
<div
|
<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,
|
active: this.props.isUsingGroupBy,
|
||||||
})}
|
})}
|
||||||
onClick={this.handleGroupBy}
|
onClick={this.handleGroupBy}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
const VisHeader = ({views, view, onToggleView, name}) => (
|
const VisHeader = ({views, view, onToggleView, name}) => (
|
||||||
<div className="graph-heading">
|
<div className="graph-heading">
|
||||||
{views.length
|
{views.length
|
||||||
? <ul className="toggle toggle-sm">
|
? <ul className="nav nav-tablist nav-tablist-sm">
|
||||||
{views.map(v => (
|
{views.map(v => (
|
||||||
<li
|
<li
|
||||||
key={v}
|
key={v}
|
||||||
onClick={() => onToggleView(v)}
|
onClick={() => onToggleView(v)}
|
||||||
className={classnames('toggle-btn ', {active: view === v})}
|
className={classnames({active: view === v})}
|
||||||
>
|
>
|
||||||
{v}
|
{_.upperFirst(v)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -108,8 +108,6 @@ const HostsTable = React.createClass({
|
||||||
hostsTitle = 'Loading Hosts...'
|
hostsTitle = 'Loading Hosts...'
|
||||||
} else if (hostsError.length) {
|
} else if (hostsError.length) {
|
||||||
hostsTitle = 'There was a problem loading hosts'
|
hostsTitle = 'There was a problem loading hosts'
|
||||||
} else if (hosts.length === 0) {
|
|
||||||
hostsTitle = 'No hosts found'
|
|
||||||
} else if (hostCount === 1) {
|
} else if (hostCount === 1) {
|
||||||
hostsTitle = `${hostCount} Host`
|
hostsTitle = `${hostCount} Host`
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,45 +121,52 @@ const HostsTable = React.createClass({
|
||||||
<SearchBar onSearch={this.updateSearchTerm} />
|
<SearchBar onSearch={this.updateSearchTerm} />
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<table className="table v-center">
|
{hostCount > 0 && !hostsError.length
|
||||||
<thead>
|
? <table className="table v-center table-highlight">
|
||||||
<tr>
|
<thead>
|
||||||
<th
|
<tr>
|
||||||
onClick={() => this.updateSort('name')}
|
<th
|
||||||
className={this.sortableClasses('name')}
|
onClick={() => this.updateSort('name')}
|
||||||
>
|
className={this.sortableClasses('name')}
|
||||||
Host
|
>
|
||||||
</th>
|
Host
|
||||||
<th
|
</th>
|
||||||
onClick={() => this.updateSort('deltaUptime')}
|
<th
|
||||||
className={this.sortableClasses('deltaUptime')}
|
onClick={() => this.updateSort('deltaUptime')}
|
||||||
style={{width: '74px'}}
|
className={this.sortableClasses('deltaUptime')}
|
||||||
>
|
style={{width: '74px'}}
|
||||||
Status
|
>
|
||||||
</th>
|
Status
|
||||||
<th
|
</th>
|
||||||
onClick={() => this.updateSort('cpu')}
|
<th
|
||||||
className={this.sortableClasses('cpu')}
|
onClick={() => this.updateSort('cpu')}
|
||||||
style={{width: '70px'}}
|
className={this.sortableClasses('cpu')}
|
||||||
>
|
style={{width: '70px'}}
|
||||||
CPU
|
>
|
||||||
</th>
|
CPU
|
||||||
<th
|
</th>
|
||||||
onClick={() => this.updateSort('load')}
|
<th
|
||||||
className={this.sortableClasses('load')}
|
onClick={() => this.updateSort('load')}
|
||||||
style={{width: '68px'}}
|
className={this.sortableClasses('load')}
|
||||||
>
|
style={{width: '68px'}}
|
||||||
Load
|
>
|
||||||
</th>
|
Load
|
||||||
<th>Apps</th>
|
</th>
|
||||||
</tr>
|
<th>Apps</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{sortedHosts.map(h => {
|
|
||||||
return <HostRow key={h.name} host={h} source={source} />
|
<tbody>
|
||||||
})}
|
{sortedHosts.map(h => {
|
||||||
</tbody>
|
return <HostRow key={h.name} host={h} source={source} />
|
||||||
</table>
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
: <div className="generic-empty-state">
|
||||||
|
<h4 style={{margin: '90px 0'}}>
|
||||||
|
No Hosts found
|
||||||
|
</h4>
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -190,21 +190,20 @@ export const HostPage = React.createClass({
|
||||||
>
|
>
|
||||||
{Object.keys(hosts).map((host, i) => {
|
{Object.keys(hosts).map((host, i) => {
|
||||||
return (
|
return (
|
||||||
<li key={i}>
|
<li className="dropdown-item" key={i}>
|
||||||
<Link
|
<Link to={`/sources/${id}/hosts/${host + appParam}`}>
|
||||||
to={`/sources/${id}/hosts/${host + appParam}`}
|
|
||||||
className="role-option"
|
|
||||||
>
|
|
||||||
{host}
|
{host}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</DashboardHeader>
|
</DashboardHeader>
|
||||||
<FancyScrollbar className={classnames({
|
<FancyScrollbar
|
||||||
'page-contents': true,
|
className={classnames({
|
||||||
'presentation-mode': inPresentationMode,
|
'page-contents': true,
|
||||||
})}>
|
'presentation-mode': inPresentationMode,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div className="container-fluid full-width dashboard">
|
<div className="container-fluid full-width dashboard">
|
||||||
{layouts.length > 0 ? this.renderLayouts(layouts) : ''}
|
{layouts.length > 0 ? this.renderLayouts(layouts) : ''}
|
||||||
</div>
|
</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) {
|
export function updateRuleValues(ruleID, trigger, values) {
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_RULE_VALUES',
|
type: 'UPDATE_RULE_VALUES',
|
||||||
|
|
|
@ -6,6 +6,8 @@ import DatabaseList from '../../data_explorer/components/DatabaseList'
|
||||||
import MeasurementList from '../../data_explorer/components/MeasurementList'
|
import MeasurementList from '../../data_explorer/components/MeasurementList'
|
||||||
import FieldList from '../../data_explorer/components/FieldList'
|
import FieldList from '../../data_explorer/components/FieldList'
|
||||||
|
|
||||||
|
import {defaultEveryFrequency} from 'src/kapacitor/constants'
|
||||||
|
|
||||||
export const DataSection = React.createClass({
|
export const DataSection = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
source: PropTypes.shape({
|
source: PropTypes.shape({
|
||||||
|
@ -27,6 +29,8 @@ export const DataSection = React.createClass({
|
||||||
groupByTime: PropTypes.func.isRequired,
|
groupByTime: PropTypes.func.isRequired,
|
||||||
toggleTagAcceptance: PropTypes.func.isRequired,
|
toggleTagAcceptance: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
onAddEvery: PropTypes.func.isRequired,
|
||||||
|
onRemoveEvery: PropTypes.func.isRequired,
|
||||||
timeRange: PropTypes.shape({}).isRequired,
|
timeRange: PropTypes.shape({}).isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -53,6 +57,11 @@ export const DataSection = React.createClass({
|
||||||
|
|
||||||
handleToggleField(field) {
|
handleToggleField(field) {
|
||||||
this.props.actions.toggleField(this.props.query.id, field, true)
|
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) {
|
handleGroupByTime(time) {
|
||||||
|
@ -61,6 +70,7 @@ export const DataSection = React.createClass({
|
||||||
|
|
||||||
handleApplyFuncsToField(fieldFunc) {
|
handleApplyFuncsToField(fieldFunc) {
|
||||||
this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc)
|
this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc)
|
||||||
|
this.props.onAddEvery(defaultEveryFrequency)
|
||||||
},
|
},
|
||||||
|
|
||||||
handleChooseTag(tag) {
|
handleChooseTag(tag) {
|
||||||
|
@ -80,13 +90,13 @@ export const DataSection = React.createClass({
|
||||||
const statement = query.rawText || buildInfluxQLQuery({lower}, query)
|
const statement = query.rawText || buildInfluxQLQuery({lower}, query)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="kapacitor-rule-section kapacitor-metric-selector">
|
<div className="rule-section">
|
||||||
<h3 className="rule-section-heading">Select a Time Series</h3>
|
<h3 className="rule-section--heading">Select a Time Series</h3>
|
||||||
<div className="rule-section-body">
|
<div className="rule-section--body">
|
||||||
<pre>
|
<pre className="rule-section--border-bottom">
|
||||||
<code
|
<code
|
||||||
className={classnames({
|
className={classnames({
|
||||||
'kapacitor-metric-placeholder': !statement,
|
'metric-placeholder': !statement,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{statement || 'Build a query below'}
|
{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">
|
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||||
<button
|
<button
|
||||||
className="btn btn-info"
|
className="btn btn-default"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
>
|
>
|
||||||
|
|
|
@ -70,6 +70,8 @@ export const KapacitorRule = React.createClass({
|
||||||
source={source}
|
source={source}
|
||||||
query={query}
|
query={query}
|
||||||
actions={queryActions}
|
actions={queryActions}
|
||||||
|
onAddEvery={this.handleAddEvery}
|
||||||
|
onRemoveEvery={this.handleRemoveEvery}
|
||||||
/>
|
/>
|
||||||
<ValuesSection
|
<ValuesSection
|
||||||
rule={rule}
|
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() {
|
validationError() {
|
||||||
const {rule, query} = this.props
|
const {rule, query} = this.props
|
||||||
if (rule.trigger === 'deadman') {
|
if (rule.trigger === 'deadman') {
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const RuleGraph = React.createClass({
|
||||||
|
|
||||||
if (!queryText) {
|
if (!queryText) {
|
||||||
return (
|
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>
|
<p>Select a <strong>Time-Series</strong> to preview on a graph</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -89,7 +89,7 @@ export const RuleHeader = React.createClass({
|
||||||
id="save-kapacitor-tooltip"
|
id="save-kapacitor-tooltip"
|
||||||
effect="solid"
|
effect="solid"
|
||||||
html={true}
|
html={true}
|
||||||
offset={{top: 2}}
|
offset={{bottom: 4}}
|
||||||
place="bottom"
|
place="bottom"
|
||||||
class="influx-tooltip kapacitor-tooltip place-bottom"
|
class="influx-tooltip kapacitor-tooltip place-bottom"
|
||||||
/>
|
/>
|
||||||
|
@ -110,31 +110,31 @@ export const RuleHeader = React.createClass({
|
||||||
onKeyDown={e => this.handleEditName(e, rule)}
|
onKeyDown={e => this.handleEditName(e, rule)}
|
||||||
onBlur={() => this.handleEditNameBlur(rule)}
|
onBlur={() => this.handleEditNameBlur(rule)}
|
||||||
placeholder="Name your rule"
|
placeholder="Name your rule"
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete={false}
|
||||||
/>
|
/>
|
||||||
: <h1
|
: <div className="page-header__left">
|
||||||
className="page-header__title page-header--editable kapacitor-theme"
|
<h1
|
||||||
onClick={this.toggleEditName}
|
className="page-header__title page-header--editable kapacitor-theme"
|
||||||
data-for="rename-kapacitor-tooltip"
|
onClick={this.toggleEditName}
|
||||||
data-tip="Click to Rename"
|
data-for="rename-kapacitor-tooltip"
|
||||||
>
|
data-tip="Click to Rename"
|
||||||
{rule.name}
|
>
|
||||||
<span className="icon pencil" />
|
{rule.name}
|
||||||
<ReactTooltip
|
<span className="icon pencil" />
|
||||||
id="rename-kapacitor-tooltip"
|
<ReactTooltip
|
||||||
delayShow={200}
|
id="rename-kapacitor-tooltip"
|
||||||
effect="solid"
|
delayShow={200}
|
||||||
html={true}
|
effect="solid"
|
||||||
offset={{top: 2}}
|
html={true}
|
||||||
place="bottom"
|
offset={{top: 2}}
|
||||||
class="influx-tooltip kapacitor-tooltip place-bottom"
|
place="bottom"
|
||||||
/>
|
class="influx-tooltip kapacitor-tooltip place-bottom"
|
||||||
</h1>
|
/>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
return name
|
||||||
<div className="page-header__left">
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -53,16 +53,16 @@ export const RuleMessage = React.createClass({
|
||||||
const selectedAlert = rule.alerts[0] || alerts[0].text
|
const selectedAlert = rule.alerts[0] || alerts[0].text
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="kapacitor-rule-section">
|
<div className="rule-section">
|
||||||
<h3 className="rule-section-heading">Alert Message</h3>
|
<h3 className="rule-section--heading">Alert Message</h3>
|
||||||
<div className="rule-section-body">
|
<div className="rule-section--body">
|
||||||
<div className="kapacitor-values-tabs">
|
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
|
||||||
<p>Send this Alert to:</p>
|
<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 => (
|
{alerts.map(alert => (
|
||||||
<li
|
<li
|
||||||
key={alert.text}
|
key={alert.text}
|
||||||
className={classnames('btn tab', {
|
className={classnames({
|
||||||
active: alert.text === selectedAlert,
|
active: alert.text === selectedAlert,
|
||||||
})}
|
})}
|
||||||
onClick={() => this.handleChooseAlert(alert)}
|
onClick={() => this.handleChooseAlert(alert)}
|
||||||
|
@ -78,25 +78,27 @@ export const RuleMessage = React.createClass({
|
||||||
rule={rule}
|
rule={rule}
|
||||||
/>
|
/>
|
||||||
{selectedAlert === 'smtp'
|
{selectedAlert === 'smtp'
|
||||||
? <div className="alert-message--email-body">
|
? <div className="rule-section--border-bottom">
|
||||||
<textarea
|
<textarea
|
||||||
className="alert-text details"
|
className="form-control form-malachite monotype rule-builder--message"
|
||||||
placeholder="Email body text goes here"
|
placeholder="Email body text goes here"
|
||||||
ref={r => (this.details = r)}
|
ref={r => (this.details = r)}
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
actions.updateDetails(rule.id, this.details.value)}
|
actions.updateDetails(rule.id, this.details.value)}
|
||||||
value={rule.details}
|
value={rule.details}
|
||||||
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
<textarea
|
<textarea
|
||||||
className="alert-text message"
|
className="form-control form-malachite monotype rule-builder--message"
|
||||||
ref={r => (this.message = r)}
|
ref={r => (this.message = r)}
|
||||||
onChange={() => actions.updateMessage(rule.id, this.message.value)}
|
onChange={() => actions.updateMessage(rule.id, this.message.value)}
|
||||||
placeholder="Example: {{ .ID }} is {{ .Level }} value: {{ index .Fields "value" }}"
|
placeholder="Example: {{ .ID }} is {{ .Level }} value: {{ index .Fields "value" }}"
|
||||||
value={rule.message}
|
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>
|
<p>Templates:</p>
|
||||||
{Object.keys(templates).map(t => {
|
{Object.keys(templates).map(t => {
|
||||||
return (
|
return (
|
||||||
|
@ -137,7 +139,15 @@ const CodeData = React.createClass({
|
||||||
const {onClickTemplate, template} = this.props
|
const {onClickTemplate, template} = this.props
|
||||||
const {label, text} = template
|
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 null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="rule-section--item alert-message--config">
|
<div className="rule-section--row rule-section--border-bottom">
|
||||||
<p>{DEFAULT_ALERT_LABELS[alert]}</p>
|
<p>{DEFAULT_ALERT_LABELS[alert]}</p>
|
||||||
<input
|
<input
|
||||||
id="alert-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"
|
type="text"
|
||||||
placeholder={DEFAULT_ALERT_PLACEHOLDERS[alert]}
|
placeholder={DEFAULT_ALERT_PLACEHOLDERS[alert]}
|
||||||
onChange={e => updateAlertNodes(rule.id, alert, e.target.value)}
|
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))
|
const initialIndex = TABS.indexOf(_.startCase(rule.trigger))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="kapacitor-rule-section">
|
<div className="rule-section">
|
||||||
<h3 className="rule-section-heading">Rule Conditions</h3>
|
<h3 className="rule-section--heading">Rule Conditions</h3>
|
||||||
<div className="rule-section-body">
|
<div className="rule-section--body">
|
||||||
<Tabs initialIndex={initialIndex} onSelect={this.handleChooseTrigger}>
|
<Tabs initialIndex={initialIndex} onSelect={this.handleChooseTrigger}>
|
||||||
<TabList isKapacitorTabs="true">
|
<TabList isKapacitorTabs="true">
|
||||||
{TABS.map(tab => <Tab key={tab}>{tab}</Tab>)}
|
{TABS.map(tab => (
|
||||||
|
<Tab key={tab} isKapacitorTab={true}>{tab}</Tab>
|
||||||
|
))}
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
|
@ -99,29 +101,38 @@ const Threshold = React.createClass({
|
||||||
const operators = mapToItems(OPERATORS, 'operator')
|
const operators = mapToItems(OPERATORS, 'operator')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="value-selector">
|
<div className="rule-section--row rule-section--border-bottom">
|
||||||
<p>Send Alert where</p>
|
<p>Send Alert where</p>
|
||||||
<span>
|
<span className="rule-builder--metric">
|
||||||
{query.fields.length ? query.fields[0].field : 'Select a Time-Series'}
|
{query.fields.length ? query.fields[0].field : 'Select a Time-Series'}
|
||||||
</span>
|
</span>
|
||||||
<p>is</p>
|
<p>is</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="size-176 dropdown-kapacitor"
|
className="dropdown-180"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
items={operators}
|
items={operators}
|
||||||
selected={operator}
|
selected={operator}
|
||||||
onChoose={this.handleDropdownChange}
|
onChoose={this.handleDropdownChange}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="form-control input-sm size-166 form-control--green"
|
className="form-control input-sm form-malachite monotype"
|
||||||
|
style={{width: '160px'}}
|
||||||
type="text"
|
type="text"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
ref={r => (this.valueInput = r)}
|
ref={r => (this.valueInput = r)}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
onKeyUp={this.handleInputChange}
|
onKeyUp={this.handleInputChange}
|
||||||
|
placeholder={
|
||||||
|
operator === 'inside range' || operator === 'outside range'
|
||||||
|
? 'Lower'
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{(operator === 'inside range' || operator === 'outside range') &&
|
{(operator === 'inside range' || operator === 'outside range') &&
|
||||||
<input
|
<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"
|
type="text"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
ref={r => (this.valueRangeInput = r)}
|
ref={r => (this.valueRangeInput = r)}
|
||||||
|
@ -162,30 +173,34 @@ const Relative = React.createClass({
|
||||||
const operators = mapToItems(OPERATORS, 'operator')
|
const operators = mapToItems(OPERATORS, 'operator')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="value-selector">
|
<div className="rule-section--row rule-section--border-bottom">
|
||||||
<p>Send Alert when</p>
|
<p>Send Alert when</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="size-106 dropdown-kapacitor"
|
className="dropdown-110"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
items={changes}
|
items={changes}
|
||||||
selected={change}
|
selected={change}
|
||||||
onChoose={this.handleDropdownChange}
|
onChoose={this.handleDropdownChange}
|
||||||
/>
|
/>
|
||||||
<p>compared to previous</p>
|
<p>compared to previous</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="size-66 dropdown-kapacitor"
|
className="dropdown-80"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
items={shifts}
|
items={shifts}
|
||||||
selected={shift}
|
selected={shift}
|
||||||
onChoose={this.handleDropdownChange}
|
onChoose={this.handleDropdownChange}
|
||||||
/>
|
/>
|
||||||
<p>is</p>
|
<p>is</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="size-176 dropdown-kapacitor"
|
className="dropdown-160"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
items={operators}
|
items={operators}
|
||||||
selected={operator}
|
selected={operator}
|
||||||
onChoose={this.handleDropdownChange}
|
onChoose={this.handleDropdownChange}
|
||||||
/>
|
/>
|
||||||
<input
|
<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)}
|
ref={r => (this.input = r)}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
onKeyUp={this.handleInputChange}
|
onKeyUp={this.handleInputChange}
|
||||||
|
@ -193,7 +208,7 @@ const Relative = React.createClass({
|
||||||
type="text"
|
type="text"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
/>
|
/>
|
||||||
<p>{change === CHANGES[1] ? '%' : ''}</p>
|
{change === CHANGES[1] ? <p>%</p> : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -219,10 +234,11 @@ const Deadman = React.createClass({
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="value-selector">
|
<div className="rule-section--row">
|
||||||
<p>Send Alert if Data is missing for</p>
|
<p>Send Alert if Data is missing for</p>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="size-66 dropdown-kapacitor"
|
className="dropdown-80"
|
||||||
|
menuClass="dropdown-malachite"
|
||||||
items={periods}
|
items={periods}
|
||||||
selected={this.props.rule.values.period}
|
selected={this.props.rule.values.period}
|
||||||
onChoose={this.handleChange}
|
onChoose={this.handleChange}
|
||||||
|
|
|
@ -14,18 +14,18 @@ class RedactedInput extends Component {
|
||||||
|
|
||||||
if (defaultValue === true && !editing) {
|
if (defaultValue === true && !editing) {
|
||||||
return (
|
return (
|
||||||
<div className="alert-value-set">
|
<div className="form-control-static redacted-input">
|
||||||
<span>
|
<span className="alert-value-set">
|
||||||
value set
|
<span className="icon checkmark" /> Value set
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
onClick={() => {
|
|
||||||
this.setState({editing: true})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
(change it)
|
|
||||||
</a>
|
|
||||||
</span>
|
</span>
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-link"
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({editing: true})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change
|
||||||
|
</button>
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -52,18 +52,21 @@ const TelegramConfig = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={this.handleSaveAlert}>
|
<form onSubmit={this.handleSaveAlert}>
|
||||||
<p className="no-user-select">
|
<div className="form-group col-xs-12">
|
||||||
You need a
|
<div className="alert alert-warning alert-icon no-user-select">
|
||||||
{' '}
|
<span className="icon triangle" />
|
||||||
<a
|
You need a
|
||||||
href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot"
|
{' '}
|
||||||
target="_blank"
|
<a
|
||||||
>
|
href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot"
|
||||||
Telegram Bot
|
target="_blank"
|
||||||
</a>
|
>
|
||||||
{' '}
|
Telegram Bot
|
||||||
to use this endpoint
|
</a>
|
||||||
</p>
|
{' '}
|
||||||
|
to use this endpoint
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="form-group col-xs-12">
|
<div className="form-group col-xs-12">
|
||||||
<label htmlFor="token">
|
<label htmlFor="token">
|
||||||
Token
|
Token
|
||||||
|
@ -100,7 +103,7 @@ const TelegramConfig = React.createClass({
|
||||||
<div className="form-group col-xs-12">
|
<div className="form-group col-xs-12">
|
||||||
<label htmlFor="parseMode">Select the alert message format</label>
|
<label htmlFor="parseMode">Select the alert message format</label>
|
||||||
<div className="form-control-static">
|
<div className="form-control-static">
|
||||||
<div className="radio">
|
<div className="radio-item">
|
||||||
<input
|
<input
|
||||||
id="parseModeMarkdown"
|
id="parseModeMarkdown"
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -111,7 +114,7 @@ const TelegramConfig = React.createClass({
|
||||||
/>
|
/>
|
||||||
<label htmlFor="parseModeMarkdown">Markdown</label>
|
<label htmlFor="parseModeMarkdown">Markdown</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="radio">
|
<div className="radio-item">
|
||||||
<input
|
<input
|
||||||
id="parseModeHTML"
|
id="parseModeHTML"
|
||||||
type="radio"
|
type="radio"
|
||||||
|
|
|
@ -19,6 +19,8 @@ export const defaultRuleConfigs = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultEveryFrequency = '30s'
|
||||||
|
|
||||||
export const OPERATORS = [
|
export const OPERATORS = [
|
||||||
'greater than',
|
'greater than',
|
||||||
'equal to or greater',
|
'equal to or greater',
|
||||||
|
@ -80,7 +82,7 @@ export const DEFAULT_ALERT_LABELS = {
|
||||||
http: 'URL:',
|
http: 'URL:',
|
||||||
tcp: 'Address:',
|
tcp: 'Address:',
|
||||||
exec: 'Add Command (Arguments separated by Spaces):',
|
exec: 'Add Command (Arguments separated by Spaces):',
|
||||||
log: 'File',
|
log: 'File:',
|
||||||
smtp: 'Email Addresses (Separated by Spaces):',
|
smtp: 'Email Addresses (Separated by Spaces):',
|
||||||
slack: 'Send alerts to Slack channel:',
|
slack: 'Send alerts to Slack channel:',
|
||||||
alerta: 'Paste Alerta TICKscript:',
|
alerta: 'Paste Alerta TICKscript:',
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import _ from 'lodash'
|
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 {bindActionCreators} from 'redux'
|
||||||
import {getActiveKapacitor, getKapacitorConfig} from 'shared/apis/index'
|
import {getActiveKapacitor, getKapacitorConfig} from 'shared/apis/index'
|
||||||
import {ALERTS, DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
import {ALERTS, DEFAULT_RULE_ID} from 'src/kapacitor/constants'
|
||||||
|
@ -23,6 +25,8 @@ export const KapacitorRulePage = React.createClass({
|
||||||
loadDefaultRule: PropTypes.func.isRequired,
|
loadDefaultRule: PropTypes.func.isRequired,
|
||||||
fetchRule: PropTypes.func.isRequired,
|
fetchRule: PropTypes.func.isRequired,
|
||||||
chooseTrigger: PropTypes.func.isRequired,
|
chooseTrigger: PropTypes.func.isRequired,
|
||||||
|
addEvery: PropTypes.func.isRequired,
|
||||||
|
removeEvery: PropTypes.func.isRequired,
|
||||||
updateRuleValues: PropTypes.func.isRequired,
|
updateRuleValues: PropTypes.func.isRequired,
|
||||||
updateMessage: PropTypes.func.isRequired,
|
updateMessage: PropTypes.func.isRequired,
|
||||||
updateAlerts: PropTypes.func.isRequired,
|
updateAlerts: PropTypes.func.isRequired,
|
||||||
|
@ -76,7 +80,7 @@ export const KapacitorRulePage = React.createClass({
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
addFlashMessage({
|
addFlashMessage({
|
||||||
type: 'error',
|
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: '',
|
message: '',
|
||||||
alerts: [],
|
alerts: [],
|
||||||
alertNodes: [],
|
alertNodes: [],
|
||||||
every: '30s',
|
every: null,
|
||||||
name: 'Untitled Rule',
|
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': {
|
case 'UPDATE_RULE_VALUES': {
|
||||||
const {ruleID, trigger, values} = action.payload
|
const {ruleID, trigger, values} = action.payload
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
|
|
|
@ -46,7 +46,7 @@ const AutoRefreshDropdown = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||||
<div
|
<div
|
||||||
className="btn btn-sm btn-info dropdown-toggle"
|
className="btn btn-sm btn-default dropdown-toggle"
|
||||||
onClick={() => self.toggleMenu()}
|
onClick={() => self.toggleMenu()}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
const ConfirmButtons = ({onConfirm, item, onCancel}) => (
|
const ConfirmButtons = ({onConfirm, item, onCancel, buttonSize}) => (
|
||||||
<div className="confirm-buttons">
|
<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" />
|
<span className="icon remove" />
|
||||||
</button>
|
</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" />
|
<span className="icon checkmark" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +28,10 @@ ConfirmButtons.propTypes = {
|
||||||
onConfirm: func.isRequired,
|
onConfirm: func.isRequired,
|
||||||
item: oneOfType([shape(), string]),
|
item: oneOfType([shape(), string]),
|
||||||
onCancel: func.isRequired,
|
onCancel: func.isRequired,
|
||||||
|
buttonSize: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfirmButtons.defaultProps = {
|
||||||
|
buttonSize: 'btn-sm',
|
||||||
|
}
|
||||||
export default ConfirmButtons
|
export default ConfirmButtons
|
||||||
|
|
|
@ -46,15 +46,15 @@ class CustomTimeRangeDropdown extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('custom-time-range', {show: isVisible})}
|
className={classnames('custom-time-range', {open: isVisible})}
|
||||||
style={{display: 'flex'}}
|
style={{display: 'flex'}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-info custom-time-range--btn"
|
className="btn btn-sm btn-default dropdown-toggle"
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
>
|
>
|
||||||
<span className="icon clock" />
|
<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" />
|
<span className="caret" />
|
||||||
</button>
|
</button>
|
||||||
<div className="custom-time--container">
|
<div className="custom-time--container">
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||||
|
|
||||||
const DeleteButton = ({onClickDelete}) => (
|
const DeleteButton = ({onClickDelete, buttonSize}) => (
|
||||||
<button
|
<button
|
||||||
className="btn btn-xs btn-danger admin-table--hidden"
|
className={classnames('btn btn-danger table--show-on-row-hover', {
|
||||||
|
[buttonSize]: buttonSize,
|
||||||
|
})}
|
||||||
onClick={onClickDelete}
|
onClick={onClickDelete}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
@ -35,7 +38,7 @@ class DeleteConfirmButtons extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {onDelete, item} = this.props
|
const {onDelete, item, buttonSize} = this.props
|
||||||
const {isConfirming} = this.state
|
const {isConfirming} = this.state
|
||||||
|
|
||||||
return isConfirming
|
return isConfirming
|
||||||
|
@ -43,8 +46,12 @@ class DeleteConfirmButtons extends Component {
|
||||||
onConfirm={onDelete}
|
onConfirm={onDelete}
|
||||||
item={item}
|
item={item}
|
||||||
onCancel={this.handleCancel}
|
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 = {
|
DeleteButton.propTypes = {
|
||||||
onClickDelete: func.isRequired,
|
onClickDelete: func.isRequired,
|
||||||
|
buttonSize: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteConfirmButtons.propTypes = {
|
DeleteConfirmButtons.propTypes = {
|
||||||
item: oneOfType([(string, shape())]),
|
item: oneOfType([(string, shape())]),
|
||||||
onDelete: func.isRequired,
|
onDelete: func.isRequired,
|
||||||
|
buttonSize: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteConfirmButtons.defaultProps = {
|
||||||
|
buttonSize: 'btn-sm',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OnClickOutside(DeleteConfirmButtons)
|
export default OnClickOutside(DeleteConfirmButtons)
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
|
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
|
||||||
|
import {ADMIN_TABLE} from 'src/admin/constants/tableSizing'
|
||||||
|
|
||||||
const DeleteConfirmTableCell = props => (
|
const DeleteConfirmTableCell = props => (
|
||||||
<td className="text-right" style={{width: '85px'}}>
|
<td className="text-right" style={{width: `${ADMIN_TABLE.colDelete}px`}}>
|
||||||
<DeleteConfirmButtons {...props} />
|
<DeleteConfirmButtons {...props} />
|
||||||
</td>
|
</td>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,10 +3,7 @@ import {Link} from 'react-router'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
import {
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||||
DROPDOWN_MENU_MAX_HEIGHT,
|
|
||||||
DROPDOWN_MENU_ITEM_THRESHOLD,
|
|
||||||
} from 'shared/constants/index'
|
|
||||||
|
|
||||||
class Dropdown extends Component {
|
class Dropdown extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -32,7 +29,7 @@ class Dropdown extends Component {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
actions: [],
|
actions: [],
|
||||||
buttonSize: 'btn-sm',
|
buttonSize: 'btn-sm',
|
||||||
buttonColor: 'btn-info',
|
buttonColor: 'btn-default',
|
||||||
menuWidth: '100%',
|
menuWidth: '100%',
|
||||||
useAutoComplete: false,
|
useAutoComplete: false,
|
||||||
}
|
}
|
||||||
|
@ -126,13 +123,14 @@ class Dropdown extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
renderShortMenu() {
|
renderMenu() {
|
||||||
const {
|
const {
|
||||||
actions,
|
actions,
|
||||||
addNew,
|
addNew,
|
||||||
items,
|
items,
|
||||||
menuWidth,
|
menuWidth,
|
||||||
menuLabel,
|
menuLabel,
|
||||||
|
menuClass,
|
||||||
useAutoComplete,
|
useAutoComplete,
|
||||||
selected,
|
selected,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
@ -143,86 +141,19 @@ class Dropdown extends Component {
|
||||||
<ul
|
<ul
|
||||||
className={classnames('dropdown-menu', {
|
className={classnames('dropdown-menu', {
|
||||||
'dropdown-menu--no-highlight': useAutoComplete,
|
'dropdown-menu--no-highlight': useAutoComplete,
|
||||||
|
[menuClass]: menuClass,
|
||||||
})}
|
})}
|
||||||
style={{width: menuWidth}}
|
style={{width: menuWidth}}
|
||||||
>
|
>
|
||||||
{menuLabel ? <li className="dropdown-header">{menuLabel}</li> : null}
|
<FancyScrollbar
|
||||||
{menuItems.map((item, i) => {
|
autoHide={false}
|
||||||
if (item.text === 'SEPARATOR') {
|
autoHeight={true}
|
||||||
return <li key={i} role="separator" className="divider" />
|
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||||
}
|
>
|
||||||
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}>
|
|
||||||
{menuLabel ? <li className="dropdown-header">{menuLabel}</li> : null}
|
{menuLabel ? <li className="dropdown-header">{menuLabel}</li> : null}
|
||||||
{menuItems.map((item, i) => {
|
{menuItems.map((item, i) => {
|
||||||
if (item.text === 'SEPARATOR') {
|
if (item.text === 'SEPARATOR') {
|
||||||
return <li key={i} role="separator" className="divider" />
|
return <li key={i} className="dropdown-divider" />
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
|
@ -240,12 +171,12 @@ class Dropdown extends Component {
|
||||||
{item.text}
|
{item.text}
|
||||||
</a>
|
</a>
|
||||||
{actions.length > 0
|
{actions.length > 0
|
||||||
? <div className="dropdown-item__actions">
|
? <div className="dropdown-actions">
|
||||||
{actions.map(action => {
|
{actions.map(action => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={action.text}
|
key={action.text}
|
||||||
className="dropdown-item__action"
|
className="dropdown-action"
|
||||||
onClick={e => this.handleAction(e, action, item)}
|
onClick={e => this.handleAction(e, action, item)}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
@ -261,8 +192,8 @@ class Dropdown extends Component {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{addNew
|
{addNew
|
||||||
? <li>
|
? <li className="multi-select--apply">
|
||||||
<Link to={addNew.url}>
|
<Link className="btn btn-xs btn-default" to={addNew.url}>
|
||||||
{addNew.text}
|
{addNew.text}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -277,6 +208,7 @@ class Dropdown extends Component {
|
||||||
items,
|
items,
|
||||||
selected,
|
selected,
|
||||||
className,
|
className,
|
||||||
|
menuClass,
|
||||||
iconName,
|
iconName,
|
||||||
buttonSize,
|
buttonSize,
|
||||||
buttonColor,
|
buttonColor,
|
||||||
|
@ -288,7 +220,10 @@ class Dropdown extends Component {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
className={classnames(`dropdown ${className}`, {open: isOpen})}
|
className={classnames('dropdown', {
|
||||||
|
open: isOpen,
|
||||||
|
[className]: className,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{useAutoComplete && isOpen
|
{useAutoComplete && isOpen
|
||||||
? <div
|
? <div
|
||||||
|
@ -314,14 +249,14 @@ class Dropdown extends Component {
|
||||||
<span className="dropdown-selected">{selected}</span>
|
<span className="dropdown-selected">{selected}</span>
|
||||||
<span className="caret" />
|
<span className="caret" />
|
||||||
</div>}
|
</div>}
|
||||||
{isOpen && menuItems.length < DROPDOWN_MENU_ITEM_THRESHOLD
|
{isOpen && menuItems.length ? this.renderMenu() : null}
|
||||||
? this.renderShortMenu()
|
|
||||||
: null}
|
|
||||||
{isOpen && menuItems.length >= DROPDOWN_MENU_ITEM_THRESHOLD
|
|
||||||
? this.renderLongMenu()
|
|
||||||
: null}
|
|
||||||
{isOpen && !menuItems.length
|
{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>
|
<li className="dropdown-empty">No matching items</li>
|
||||||
</ul>
|
</ul>
|
||||||
: null}
|
: null}
|
||||||
|
@ -358,6 +293,7 @@ Dropdown.propTypes = {
|
||||||
buttonColor: string,
|
buttonColor: string,
|
||||||
menuWidth: string,
|
menuWidth: string,
|
||||||
menuLabel: string,
|
menuLabel: string,
|
||||||
|
menuClass: string,
|
||||||
useAutoComplete: bool,
|
useAutoComplete: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,21 +9,34 @@ class FancyScrollbar extends Component {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
autoHide: true,
|
autoHide: true,
|
||||||
|
autoHeight: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {autoHide, children, className} = this.props
|
const {autoHide, autoHeight, children, className, maxHeight} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scrollbars
|
<Scrollbars
|
||||||
className={classnames('fancy-scroll--container', {[className]: className})}
|
className={classnames('fancy-scroll--container', {
|
||||||
|
[className]: className,
|
||||||
|
})}
|
||||||
autoHide={autoHide}
|
autoHide={autoHide}
|
||||||
autoHideTimeout={1000}
|
autoHideTimeout={1000}
|
||||||
autoHideDuration={250}
|
autoHideDuration={250}
|
||||||
renderTrackHorizontal={props => <div {...props} className="fancy-scroll--track-h"/>}
|
autoHeight={autoHeight}
|
||||||
renderTrackVertical={props => <div {...props} className="fancy-scroll--track-v"/>}
|
autoHeightMax={maxHeight}
|
||||||
renderThumbHorizontal={props => <div {...props} className="fancy-scroll--thumb-h"/>}
|
renderTrackHorizontal={props => (
|
||||||
renderThumbVertical={props => <div {...props} className="fancy-scroll--thumb-v"/>}
|
<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}
|
{children}
|
||||||
</Scrollbars>
|
</Scrollbars>
|
||||||
|
@ -31,12 +44,14 @@ class FancyScrollbar extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {bool, node, string} = PropTypes
|
const {bool, node, number, string} = PropTypes
|
||||||
|
|
||||||
FancyScrollbar.propTypes = {
|
FancyScrollbar.propTypes = {
|
||||||
children: node.isRequired,
|
children: node.isRequired,
|
||||||
className: string,
|
className: string,
|
||||||
autoHide: bool,
|
autoHide: bool,
|
||||||
|
autoHeight: bool,
|
||||||
|
maxHeight: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FancyScrollbar
|
export default FancyScrollbar
|
||||||
|
|
|
@ -83,7 +83,7 @@ export const LayoutRenderer = React.createClass({
|
||||||
return text
|
return text
|
||||||
},
|
},
|
||||||
|
|
||||||
renderRefreshingGraph(type, queries) {
|
renderRefreshingGraph(type, queries, cellHeight) {
|
||||||
const {timeRange, autoRefresh, templates} = this.props
|
const {timeRange, autoRefresh, templates} = this.props
|
||||||
|
|
||||||
if (type === 'single-stat') {
|
if (type === 'single-stat') {
|
||||||
|
@ -92,6 +92,7 @@ export const LayoutRenderer = React.createClass({
|
||||||
queries={[queries[0]]}
|
queries={[queries[0]]}
|
||||||
templates={templates}
|
templates={templates}
|
||||||
autoRefresh={autoRefresh}
|
autoRefresh={autoRefresh}
|
||||||
|
cellHeight={cellHeight}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +159,7 @@ export const LayoutRenderer = React.createClass({
|
||||||
shouldNotBeEditable={shouldNotBeEditable}
|
shouldNotBeEditable={shouldNotBeEditable}
|
||||||
cell={cell}
|
cell={cell}
|
||||||
>
|
>
|
||||||
{this.renderRefreshingGraph(cell.type, queries)}
|
{this.renderRefreshingGraph(cell.type, queries, cell.h)}
|
||||||
</NameableGraph>
|
</NameableGraph>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import React, {Component, PropTypes} from 'react'
|
import React, {Component, PropTypes} from 'react'
|
||||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import _ from 'lodash'
|
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}) => {
|
const labelText = ({localSelectedItems, isOpen, label}) => {
|
||||||
if (label) {
|
if (label) {
|
||||||
return label
|
return label
|
||||||
|
@ -67,20 +71,18 @@ class MultiSelectDropdown extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {localSelectedItems, isOpen} = this.state
|
const {localSelectedItems, isOpen} = this.state
|
||||||
const {label} = this.props
|
const {label, buttonSize, buttonColor, customClass, iconName} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={classnames(`dropdown ${customClass}`, {open: isOpen})}>
|
||||||
className={classnames('dropdown multi-select-dropdown', {open: isOpen})}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
onClick={::this.toggleMenu}
|
onClick={::this.toggleMenu}
|
||||||
className="btn btn-xs btn-info dropdown-toggle"
|
className={`dropdown-toggle btn ${buttonSize} ${buttonColor}`}
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
<div className="multi-select-dropdown__label">
|
{iconName ? <span className={`icon ${iconName}`} /> : null}
|
||||||
|
<span className="dropdown-selected">
|
||||||
{labelText({localSelectedItems, isOpen, label})}
|
{labelText({localSelectedItems, isOpen, label})}
|
||||||
</div>
|
</span>
|
||||||
<span className="caret" />
|
<span className="caret" />
|
||||||
</div>
|
</div>
|
||||||
{this.renderMenu()}
|
{this.renderMenu()}
|
||||||
|
@ -92,33 +94,36 @@ class MultiSelectDropdown extends Component {
|
||||||
const {items} = this.props
|
const {items} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dropdown-options">
|
<ul className="dropdown-menu">
|
||||||
<div
|
<li className="multi-select--apply">
|
||||||
className="multi-select-dropdown__apply"
|
<button
|
||||||
onClick={this.onApplyFunctions}
|
className="btn btn-xs btn-info"
|
||||||
style={{listStyle: 'none'}}
|
onClick={this.onApplyFunctions}
|
||||||
>
|
>
|
||||||
<div className="btn btn-xs btn-info btn-block">Apply</div>
|
Apply
|
||||||
</div>
|
</button>
|
||||||
<ul
|
</li>
|
||||||
className="dropdown-menu multi-select-dropdown__menu"
|
<FancyScrollbar
|
||||||
aria-labelledby="dropdownMenu1"
|
autoHide={false}
|
||||||
|
autoHeight={true}
|
||||||
|
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||||
>
|
>
|
||||||
{items.map((listItem, i) => {
|
{items.map((listItem, i) => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={i}
|
key={i}
|
||||||
className={classnames('multi-select-dropdown__item', {
|
className={classnames('multi-select--item', {
|
||||||
active: this.isSelected(listItem),
|
checked: this.isSelected(listItem),
|
||||||
})}
|
})}
|
||||||
onClick={_.wrap(listItem, this.onSelect)}
|
onClick={_.wrap(listItem, this.onSelect)}
|
||||||
>
|
>
|
||||||
<a href="#">{listItem}</a>
|
<div className="multi-select--checkbox" />
|
||||||
|
<span>{listItem}</span>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</FancyScrollbar>
|
||||||
</div>
|
</ul>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +135,15 @@ MultiSelectDropdown.propTypes = {
|
||||||
items: arrayOf(string.isRequired).isRequired,
|
items: arrayOf(string.isRequired).isRequired,
|
||||||
selectedItems: arrayOf(string.isRequired).isRequired,
|
selectedItems: arrayOf(string.isRequired).isRequired,
|
||||||
label: string,
|
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)
|
export default OnClickOutside(MultiSelectDropdown)
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
import shallowCompare from 'react-addons-shallow-compare'
|
import shallowCompare from 'react-addons-shallow-compare'
|
||||||
import lastValues from 'src/shared/parsing/lastValues'
|
import lastValues from 'src/shared/parsing/lastValues'
|
||||||
|
|
||||||
|
const SMALL_CELL_HEIGHT = 1
|
||||||
|
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'LineGraph',
|
displayName: 'LineGraph',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
data: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
data: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
isFetchingInitially: PropTypes.bool,
|
isFetchingInitially: PropTypes.bool,
|
||||||
|
cellHeight: PropTypes.number,
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
@ -15,7 +19,7 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
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 data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||||
if (this.props.isFetchingInitially) {
|
if (this.props.isFetchingInitially) {
|
||||||
|
@ -33,7 +37,13 @@ export default React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="single-stat">
|
<div className="single-stat">
|
||||||
{roundedValue}
|
<span
|
||||||
|
className={classnames('single-stat--value', {
|
||||||
|
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{roundedValue}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,9 +8,20 @@ export const Tab = React.createClass({
|
||||||
onClick: func,
|
onClick: func,
|
||||||
isDisabled: bool,
|
isDisabled: bool,
|
||||||
isActive: bool,
|
isActive: bool,
|
||||||
|
isKapacitorTab: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('btn tab', {active: this.props.isActive})}
|
className={classnames('btn tab', {active: this.props.isActive})}
|
||||||
|
@ -47,9 +58,11 @@ export const TabList = React.createClass({
|
||||||
|
|
||||||
if (this.props.isKapacitorTabs === 'true') {
|
if (this.props.isKapacitorTabs === 'true') {
|
||||||
return (
|
return (
|
||||||
<div className="kapacitor-values-tabs">
|
<div className="rule-section--row rule-section--row-first rule-section--border-bottom">
|
||||||
<p>Alert Type</p>
|
<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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import OnClickOutside from 'shared/components/OnClickOutside'
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
|
||||||
import timeRanges from 'hson!../data/timeRanges.hson'
|
import timeRanges from 'hson!../data/timeRanges.hson'
|
||||||
|
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||||
|
|
||||||
const TimeRangeDropdown = React.createClass({
|
const TimeRangeDropdown = React.createClass({
|
||||||
autobind: false,
|
autobind: false,
|
||||||
|
@ -58,7 +59,7 @@ const TimeRangeDropdown = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||||
<div
|
<div
|
||||||
className="btn btn-sm btn-info dropdown-toggle"
|
className="btn btn-sm btn-default dropdown-toggle"
|
||||||
onClick={() => self.toggleMenu()}
|
onClick={() => self.toggleMenu()}
|
||||||
>
|
>
|
||||||
<span className="icon clock" />
|
<span className="icon clock" />
|
||||||
|
@ -67,8 +68,12 @@ const TimeRangeDropdown = React.createClass({
|
||||||
</span>
|
</span>
|
||||||
<span className="caret" />
|
<span className="caret" />
|
||||||
</div>
|
</div>
|
||||||
<ul className="dropdown-menu" style={{height: '270px'}}>
|
<ul className="dropdown-menu">
|
||||||
<FancyScrollbar>
|
<FancyScrollbar
|
||||||
|
autoHide={false}
|
||||||
|
autoHeight={true}
|
||||||
|
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||||
|
>
|
||||||
<li className="dropdown-header">Time Range</li>
|
<li className="dropdown-header">Time Range</li>
|
||||||
{timeRanges.map(item => {
|
{timeRanges.map(item => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PropTypes} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
const YesNoButtons = ({onConfirm, onCancel}) => (
|
const YesNoButtons = ({onConfirm, onCancel, buttonSize}) => (
|
||||||
<div>
|
<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" />
|
<span className="icon remove" />
|
||||||
</button>
|
</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" />
|
<span className="icon checkmark" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const {func} = PropTypes
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
YesNoButtons.propTypes = {
|
YesNoButtons.propTypes = {
|
||||||
onConfirm: func.isRequired,
|
onConfirm: func.isRequired,
|
||||||
onCancel: func.isRequired,
|
onCancel: func.isRequired,
|
||||||
|
buttonSize: string,
|
||||||
|
}
|
||||||
|
YesNoButtons.defaultProps = {
|
||||||
|
buttonSize: 'btn-sm',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default YesNoButtons
|
export default YesNoButtons
|
||||||
|
|
|
@ -379,8 +379,7 @@ export const STROKE_WIDTH = {
|
||||||
light: 1.5,
|
light: 1.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DROPDOWN_MENU_MAX_HEIGHT = '270px'
|
export const DROPDOWN_MENU_MAX_HEIGHT = 240
|
||||||
export const DROPDOWN_MENU_ITEM_THRESHOLD = 10
|
|
||||||
|
|
||||||
export const HEARTBEAT_INTERVAL = 10000 // ms
|
export const HEARTBEAT_INTERVAL = 10000 // ms
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ const kapacitorDropdown = (
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="dropdown-160"
|
className="dropdown-260"
|
||||||
buttonColor="btn-info"
|
buttonColor="btn-default"
|
||||||
buttonSize="btn-xs"
|
buttonSize="btn-xs"
|
||||||
items={kapacitorItems}
|
items={kapacitorItems}
|
||||||
onChoose={item => setActiveKapacitor(item.kapacitor)}
|
onChoose={item => setActiveKapacitor(item.kapacitor)}
|
||||||
|
@ -87,7 +87,7 @@ const InfluxTable = ({
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<table className="table v-center margin-bottom-zero">
|
<table className="table v-center margin-bottom-zero table-highlight">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
@ -131,7 +131,7 @@ const InfluxTable = ({
|
||||||
Connect
|
Connect
|
||||||
</Link>}
|
</Link>}
|
||||||
<button
|
<button
|
||||||
className="btn btn-danger btn-xs"
|
className="btn btn-danger btn-xs btn-square"
|
||||||
onClick={() => handleDeleteSource(s)}
|
onClick={() => handleDeleteSource(s)}
|
||||||
>
|
>
|
||||||
<span className="icon trash" />
|
<span className="icon trash" />
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
// Base Theme
|
// Base Theme
|
||||||
@import 'theme/bootstrap';
|
@import 'theme/bootstrap';
|
||||||
@import 'theme/bootstrap-theme';
|
@import 'theme/bootstrap-theme';
|
||||||
@import 'theme/theme-dark';
|
|
||||||
@import 'theme/reset';
|
@import 'theme/reset';
|
||||||
|
|
||||||
// External
|
// External
|
||||||
|
@ -23,18 +22,16 @@
|
||||||
@import 'layout/page-header';
|
@import 'layout/page-header';
|
||||||
@import 'layout/sidebar';
|
@import 'layout/sidebar';
|
||||||
@import 'layout/overlay';
|
@import 'layout/overlay';
|
||||||
|
@import 'layout/flash-messages';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import 'components/confirm-buttons';
|
@import 'components/confirm-buttons';
|
||||||
@import 'components/custom-time-range';
|
@import 'components/custom-time-range';
|
||||||
@import 'components/dropdown';
|
|
||||||
@import 'components/dygraphs';
|
@import 'components/dygraphs';
|
||||||
@import 'components/flash-messages';
|
|
||||||
@import 'components/flip-toggle';
|
@import 'components/flip-toggle';
|
||||||
@import 'components/graph-tips';
|
@import 'components/graph-tips';
|
||||||
@import 'components/graph';
|
@import 'components/graph';
|
||||||
@import 'components/input-tag-list';
|
@import 'components/input-tag-list';
|
||||||
@import 'components/multi-select-dropdown';
|
|
||||||
@import 'components/page-header-dropdown';
|
@import 'components/page-header-dropdown';
|
||||||
@import 'components/page-header-editable';
|
@import 'components/page-header-editable';
|
||||||
@import 'components/page-spinner';
|
@import 'components/page-spinner';
|
||||||
|
|
|
@ -15,7 +15,4 @@
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
span.icon {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -6,16 +6,6 @@
|
||||||
.custom-time-range {
|
.custom-time-range {
|
||||||
position: relative;
|
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 {
|
.custom-time--container {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -242,8 +232,8 @@ $rd-cell-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Show State */
|
/* Open State */
|
||||||
.custom-time-range.show {
|
.custom-time-range.open {
|
||||||
.custom-time--container {
|
.custom-time--container {
|
||||||
display: flex;
|
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;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 60px;
|
@include no-user-select();
|
||||||
font-weight: 300;
|
|
||||||
color: $c-pool;
|
&.graph-single-stat {
|
||||||
display: flex;
|
top: 0;
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
}
|
||||||
height: 100%;
|
.single-stat--value {
|
||||||
text-align: center;
|
position: absolute;
|
||||||
|
top: calc(50% - 15px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%,-50%);
|
||||||
font-size: 54px;
|
font-size: 54px;
|
||||||
|
line-height: 54px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: $c-pool;
|
color: $c-pool;
|
||||||
|
|
||||||
&.graph-single-stat {
|
&.single-stat--small {
|
||||||
position: absolute;
|
font-size: 38px;
|
||||||
width: 100%;
|
line-height: 38px;
|
||||||
top: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
.dropdown-toggle {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
padding-right: (11px + 12px); // caret width + offset
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
width: auto;
|
width: auto;
|
||||||
border: 0;
|
border: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
padding-right: (11px + 12px); // caret width + offset
|
|
||||||
font-size: $page-header-size;
|
font-size: $page-header-size;
|
||||||
font-weight: $page-header-weight;
|
font-weight: $page-header-weight;
|
||||||
transition: color 0.25s ease;
|
transition: color 0.25s ease;
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
font-weight: $page-header-weight;
|
font-weight: $page-header-weight;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
|
||||||
top: -1px;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
color: $c-pool;
|
color: $c-pool;
|
||||||
|
|
|
@ -114,9 +114,9 @@
|
||||||
transition: color 0.25s ease;
|
transition: color 0.25s ease;
|
||||||
color: $g9-mountain;
|
color: $g9-mountain;
|
||||||
}
|
}
|
||||||
& > input {
|
& > input.form-control {
|
||||||
order: 2;
|
order: 2;
|
||||||
padding: 0 8px 0 24px;
|
padding-left: 24px;
|
||||||
border-color: $g6-smoke !important;
|
border-color: $g6-smoke !important;
|
||||||
|
|
||||||
&:hover {border-color: $g7-graphite !important;}
|
&:hover {border-color: $g7-graphite !important;}
|
||||||
|
@ -172,13 +172,6 @@
|
||||||
.query-builder--list-item {padding: 0;}
|
.query-builder--list-item {padding: 0;}
|
||||||
.query-builder--filter {margin-bottom: 4px;}
|
.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 */
|
/* Toggle for grouping by tags in tags list */
|
||||||
.group-by-tag {
|
.group-by-tag {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
@ -198,9 +191,6 @@
|
||||||
.query-builder--list-item.active .group-by-tag {
|
.query-builder--list-item.active .group-by-tag {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.query-builder--db-dropdown {
|
.query-builder--db-dropdown {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
|
@ -88,14 +88,6 @@
|
||||||
.dropdown.query-editor--templates {
|
.dropdown.query-editor--templates {
|
||||||
margin: 0 4px 0 0 ;
|
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 {
|
.dropdown-menu {
|
||||||
left: initial;
|
left: initial;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
.query-maker {
|
.query-maker {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0 $explorer-page-padding;
|
margin: 0 $page-wrapper-padding;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
.alert-value-set {
|
.redacted-input {
|
||||||
padding: 6px 13px 0;
|
height: 38px;
|
||||||
|
align-items: center;
|
||||||
span a {
|
justify-content: space-between;
|
||||||
margin-left: 10px;
|
}
|
||||||
}
|
.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
|
// 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
|
Sortable Tables
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
|
@ -36,7 +36,7 @@ $template-control-dropdown-min-width: 146px;
|
||||||
@include no-user-select();
|
@include no-user-select();
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.template-control--manage {
|
button.btn.template-control--manage {
|
||||||
margin: 7px 8px;
|
margin: 7px 8px;
|
||||||
}
|
}
|
||||||
.template-control--controls {
|
.template-control--controls {
|
||||||
|
@ -64,38 +64,12 @@ $template-control-dropdown-min-width: 146px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-toggle {
|
.dropdown-toggle {
|
||||||
border-radius: 0 0 $radius-small $radius-small;
|
border-radius: 0 0 $radius-small $radius-small;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: $code-font;
|
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 {
|
.template-control--label {
|
||||||
@include no-user-select();
|
@include no-user-select();
|
||||||
|
|
|
@ -191,11 +191,6 @@ $tvmp-table-gutter: 8px;
|
||||||
|
|
||||||
.btn-edit {
|
.btn-edit {
|
||||||
order: 1;
|
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;}
|
> .btn {margin-left: $tvmp-table-gutter;}
|
||||||
|
@ -204,17 +199,9 @@ $tvmp-table-gutter: 8px;
|
||||||
/* Janky, but doing this quick & dirty for now */
|
/* Janky, but doing this quick & dirty for now */
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
order: 2;
|
order: 2;
|
||||||
height: 30px !important;
|
|
||||||
line-height: 30px !important;
|
|
||||||
padding: 0 9px !important;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
}
|
||||||
.confirm-buttons > .btn {
|
.confirm-buttons > .btn {
|
||||||
height: 30px !important;
|
|
||||||
width: 30px !important;
|
|
||||||
margin-left: $tvmp-table-gutter !important;
|
margin-left: $tvmp-table-gutter !important;
|
||||||
font-size: 13px;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
}
|
||||||
/* Hide the edit button when confirming a delete */
|
/* Hide the edit button when confirming a delete */
|
||||||
.confirm-buttons + .btn-edit {
|
.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 {
|
.page-header__right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
> *:only-child {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.page-header__left {
|
.page-header__left {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
@ -49,10 +45,15 @@ $page-header-weight: 400 !important;
|
||||||
.page-header__right {
|
.page-header__right {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
> * {
|
> * {
|
||||||
margin: 0 0 0 4px;
|
margin: 0 0 0 4px !important;
|
||||||
|
|
||||||
|
&:only-child {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.page-header__title {
|
.page-header__title {
|
||||||
|
letter-spacing: 0;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-size: $page-header-size;
|
font-size: $page-header-size;
|
||||||
font-weight: $page-header-weight;
|
font-weight: $page-header-weight;
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
$sidebar-width: 60px;
|
||||||
$sidebar-menu-gutter: 30px;
|
$sidebar-menu-gutter: 30px;
|
||||||
$sidebar-menu-indent: 13px;
|
$sidebar-menu-indent: 13px;
|
||||||
$sidebar-radius: 4px;
|
|
||||||
|
|
||||||
$sidebar-hover: $g8-storm;
|
$sidebar-hover: $g8-storm;
|
||||||
$sidebar-item-hover: $g7-graphite;
|
$sidebar-item-hover: $g7-graphite;
|
||||||
|
@ -21,38 +21,6 @@ $sidebar-active-accent: $c-laser;
|
||||||
$sidebar-logo-bg: $g17-whisper;
|
$sidebar-logo-bg: $g17-whisper;
|
||||||
$sidebar-logo-color: $g8-storm;
|
$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 styles
|
||||||
.sidebar {
|
.sidebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -176,7 +144,7 @@ $sidebar-logo-color: $g8-storm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__menu {
|
&__menu {
|
||||||
border-radius: 0 $sidebar-radius $sidebar-radius 0;
|
border-radius: 0 $radius $radius 0;
|
||||||
background: $sidebar-hover;
|
background: $sidebar-hover;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.25s ease;
|
transition: opacity 0.25s ease;
|
||||||
|
@ -237,12 +205,12 @@ $sidebar-logo-color: $g8-storm;
|
||||||
|
|
||||||
// Rounding top outside corner to match container
|
// Rounding top outside corner to match container
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-right-radius: $sidebar-radius;
|
border-top-right-radius: $radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rounding bottom outside corner of match container
|
// Rounding bottom outside corner of match container
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom-right-radius: $sidebar-radius;
|
border-bottom-right-radius: $radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for sub-navigation
|
// Used for sub-navigation
|
||||||
|
@ -314,5 +282,4 @@ $sidebar-logo-color: $g8-storm;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
padding: 0px $sidebar-menu-gutter;
|
padding: 0px $sidebar-menu-gutter;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -109,6 +109,9 @@ $scrollbar-offset: 3px;
|
||||||
-webkit-user-select: none !important;
|
-webkit-user-select: none !important;
|
||||||
-ms-user-select: none !important;
|
-ms-user-select: none !important;
|
||||||
-o-user-select: none !important;
|
-o-user-select: none !important;
|
||||||
|
&, &:hover {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.no-user-select {
|
.no-user-select {
|
||||||
user-select: none !important;
|
user-select: none !important;
|
||||||
|
@ -116,4 +119,7 @@ $scrollbar-offset: 3px;
|
||||||
-webkit-user-select: none !important;
|
-webkit-user-select: none !important;
|
||||||
-ms-user-select: none !important;
|
-ms-user-select: none !important;
|
||||||
-o-user-select: none !important;
|
-o-user-select: none !important;
|
||||||
|
&, &:hover {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
$radius: 4px;
|
$radius: 4px;
|
||||||
$radius-small: 3px;
|
$radius-small: 3px;
|
||||||
|
$page-wrapper-padding: 60px;
|
||||||
// Sidebar
|
|
||||||
$sidebar-width: 60px;
|
|
||||||
$page-wrapper-padding: 58px;
|
|
||||||
$page-wrapper-max-width: 1300px;
|
$page-wrapper-max-width: 1300px;
|
||||||
$current-user-height: 56px;
|
|
||||||
$cluster-switcher-height: 56px;
|
|
||||||
$chronograf-page-header-height: 60px;
|
$chronograf-page-header-height: 60px;
|
||||||
$sidebar-tier1-height: 56px;
|
|
||||||
|
|
||||||
// Data Explorer
|
|
||||||
$explorer-page-padding: $page-wrapper-padding;
|
|
||||||
|
|
|
@ -12,22 +12,7 @@
|
||||||
*/
|
*/
|
||||||
.admin-tabs {
|
.admin-tabs {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
& + div {padding-left: 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.admin-tabs .btn-group {
|
.admin-tabs .btn-group {
|
||||||
margin: 0;
|
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
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
*/
|
*/
|
||||||
.admin-table {
|
.admin-table .dropdown-toggle {
|
||||||
.multi-select-dropdown {
|
background-color: transparent;
|
||||||
width: 100%;
|
font-weight: 600;
|
||||||
min-width: 150px;
|
color: $g14-chromium;
|
||||||
}
|
transition: none !important;
|
||||||
.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;
|
|
||||||
|
|
||||||
.caret {opacity: 0;}
|
.caret {opacity: 0;}
|
||||||
.multi-select-dropdown__label {left: 0;}
|
}
|
||||||
}
|
.admin-table--multi-select-empty .dropdown-toggle {
|
||||||
.open .dropdown-toggle .multi-select-dropdown__label {left: 9px;}
|
color: $g8-storm;
|
||||||
tbody tr:hover {
|
}
|
||||||
.admin-table--hidden {
|
.admin-table tbody tr:hover .dropdown-toggle {
|
||||||
visibility: visible;
|
color: $g20-white !important;
|
||||||
}
|
background-color: $c-pool;
|
||||||
.dropdown-toggle {
|
|
||||||
color: $g20-white !important;
|
|
||||||
background-color: $c-pool;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
.caret {opacity: 1;}
|
.caret {opacity: 1;}
|
||||||
.multi-select-dropdown__label {left: 9px;}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transition: background-color 0.25s ease;
|
transition: background-color 0.25s ease;
|
||||||
background-color: $c-laser;
|
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;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.admin-table--edit-row {
|
table > tbody > tr > td.admin-table--left-offset,
|
||||||
background-color: $g4-onyx;
|
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 {
|
.admin-table--change-pw {
|
||||||
width: 100%;
|
display: flex;
|
||||||
margin: 0 !important;
|
flex-wrap: nowrap;
|
||||||
display: flex !important;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
> input {
|
.form-control {
|
||||||
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;
|
|
||||||
margin: 0 4px 0 0;
|
margin: 0 4px 0 0;
|
||||||
min-width: 110px;
|
flex: 1 0 0;
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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 {
|
> .form-control {
|
||||||
display: block !important;
|
flex: 1 0 0;
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Database Manager
|
||||||
|
----------------------------------------------
|
||||||
|
*/
|
||||||
.db-manager {
|
.db-manager {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
.db-manager-header--actions {
|
&:last-child {margin-bottom: 0;}
|
||||||
display: none;
|
.db-manager-header--actions {display: none;}
|
||||||
}
|
&:hover .db-manager-header--actions {display: block;}
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
&:hover .db-manager-header--actions {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.db-manager-header {
|
.db-manager-header {
|
||||||
padding: 0 11px;
|
padding: 0 11px;
|
||||||
|
@ -184,21 +146,18 @@
|
||||||
h4 {
|
h4 {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
color: $c-potassium;
|
color: $c-potassium;
|
||||||
font-size: 17px;
|
font-size: 16px;
|
||||||
font-family: $code-font;
|
font-family: $code-font;
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.db-manager-header--edit {
|
.db-manager-header--edit {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
height: 22px;
|
margin: 0 8px 0 0;
|
||||||
padding: 0 6px;
|
flex: 1 0 0;
|
||||||
margin: 0 4px 0 0;
|
|
||||||
min-width: 110px;
|
|
||||||
font-size: 12px;
|
|
||||||
width: 50%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,20 +165,6 @@
|
||||||
background-color: $g4-onyx;
|
background-color: $g4-onyx;
|
||||||
padding: 9px 11px;
|
padding: 9px 11px;
|
||||||
border-radius: 0 0 $radius-small $radius-small;
|
border-radius: 0 0 $radius-small $radius-small;
|
||||||
}
|
|
||||||
|
.table-highlight > tbody > tr:hover {background-color: $g5-pepper;}
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,13 +145,13 @@ $dash-graph-options-arrow: 8px;
|
||||||
height: $dash-graph-heading;
|
height: $dash-graph-heading;
|
||||||
line-height: $dash-graph-heading;
|
line-height: $dash-graph-heading;
|
||||||
width: calc(100% - 50px);
|
width: calc(100% - 50px);
|
||||||
margin-left: 16px;
|
padding-left: 16px;
|
||||||
transition:
|
transition:
|
||||||
color 0.25s ease,
|
color 0.25s ease,
|
||||||
background-color 0.25s ease,
|
background-color 0.25s ease,
|
||||||
border-color 0.25s ease;
|
border-color 0.25s ease;
|
||||||
}
|
}
|
||||||
.dash-graph--name-edit {
|
input.form-control.dash-graph--name-edit {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
width: calc(100% - 42px);
|
width: calc(100% - 42px);
|
||||||
|
@ -166,6 +166,7 @@ $dash-graph-options-arrow: 8px;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
> .btn {
|
> .btn {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
|
@ -197,6 +198,7 @@ $dash-graph-options-arrow: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition-property: all;
|
transition-property: all;
|
||||||
|
@ -208,6 +210,8 @@ $dash-graph-options-arrow: 8px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
background-color: $g5-pepper;
|
background-color: $g5-pepper;
|
||||||
padding: 0 11px;
|
padding: 0 11px;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
color: $g15-platinum;
|
color: $g15-platinum;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition:
|
transition:
|
||||||
|
|
|
@ -1,47 +1,230 @@
|
||||||
/*
|
/*
|
||||||
Kapacitor Rule Builder
|
Kapacitor Rule Builder
|
||||||
----------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
$kapacitor-page-padding: ($chronograf-page-header-height / 2);
|
$rule-builder--accent-color: $c-rainforest;
|
||||||
$kapacitor-page-gutter: 46px;
|
$rule-builder--left-gutter: 46px;
|
||||||
$kapacitor-dot-size: 18px;
|
$rule-builder--section-gap: ($chronograf-page-header-height / 2);
|
||||||
$kapacitor-line-width: 3px;
|
$rule-builder--section-bg: $g3-castle;
|
||||||
$metric-selector-height: 156px;
|
$rule-builder--section-border: $g2-kevlar;
|
||||||
$kap-padding-sm: 8px;
|
$rule-builder--dot: 18px;
|
||||||
$kap-padding-md: 13px;
|
$rule-builder--accent-line-width: 3px;
|
||||||
$kap-padding-lg: 24px;
|
$rule-builder--accent-line-color: $g5-pepper;
|
||||||
$kap-radius-lg: 5px;
|
$rule-builder--font-size: 13px;
|
||||||
$kap-input-height: 30px;
|
$rule-builder--query-builder-height: 240px;
|
||||||
|
|
||||||
$kapacitor-graphic-color: $g3-castle;
|
$rule-builder--padding-sm: 8px;
|
||||||
$kapacitor-divider-color: $g2-kevlar;
|
$rule-builder--padding-md: 13px;
|
||||||
$kapacitor-accent: $c-rainforest;
|
$rule-builder--padding-lg: 24px;
|
||||||
$kap-line-color: $g5-pepper;
|
$rule-builder--radius-lg: 5px;
|
||||||
$kap-dot-color: $c-rainforest;
|
|
||||||
|
|
||||||
$kapacitor-font-sm: 13px;
|
|
||||||
|
|
||||||
.rule-builder {
|
.rule-builder {
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
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 {
|
.rule-builder--graph {
|
||||||
margin-left: $kapacitor-page-gutter;
|
margin-left: $rule-builder--left-gutter;
|
||||||
width: calc(100% - #{$kapacitor-page-gutter});
|
width: calc(100% - #{$rule-builder--left-gutter});
|
||||||
background-color: $kapacitor-graphic-color;
|
background-color: $rule-builder--section-bg;
|
||||||
border-radius: 0 0 $kap-radius-lg $kap-radius-lg;
|
border-radius: 0 0 $rule-builder--radius-lg $rule-builder--radius-lg;
|
||||||
padding: 0 $kap-padding-sm;
|
padding: 0 $rule-builder--padding-sm;
|
||||||
height: (300px + ($kap-padding-sm * 2));
|
height: (300px + ($rule-builder--padding-sm * 2));
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: $kap-padding-sm;
|
left: $rule-builder--padding-sm;
|
||||||
width: calc(100% - #{($kap-padding-sm * 2)});
|
width: calc(100% - #{($rule-builder--padding-sm * 2)});
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
|
@ -57,11 +240,11 @@ $kapacitor-font-sm: 13px;
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: $kapacitor-line-width;
|
width: $rule-builder--accent-line-width;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: $kap-line-color;
|
background-color: $rule-builder--accent-line-color;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: (($kapacitor-dot-size / 2) - $kapacitor-page-gutter);
|
left: (($rule-builder--dot / 2) - $rule-builder--left-gutter);
|
||||||
}
|
}
|
||||||
.container--dygraph-legend {
|
.container--dygraph-legend {
|
||||||
background-color: $g5-pepper;
|
background-color: $g5-pepper;
|
||||||
|
@ -71,7 +254,7 @@ $kapacitor-font-sm: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rule-preview--graph-empty {
|
.rule-builder--graph-empty {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -96,378 +279,42 @@ $kapacitor-font-sm: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-section-heading {
|
/*
|
||||||
margin: 0;
|
Section 3 - Rule Message
|
||||||
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();
|
|
||||||
|
|
||||||
&:before,
|
textarea.rule-builder--message {
|
||||||
&:after {
|
border-color: $rule-builder--section-bg;
|
||||||
content: '';
|
background-color: $rule-builder--section-bg;
|
||||||
display: block;
|
padding: $rule-builder--padding-sm ($rule-builder--padding-lg - 2px);
|
||||||
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%;
|
|
||||||
height: 100px;
|
height: 100px;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
max-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;
|
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 {
|
&:hover {
|
||||||
border-color: $g4-onyx;
|
color: $rule-builder--accent-color;
|
||||||
}
|
cursor: pointer;
|
||||||
&: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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-message--endpoint {
|
/*
|
||||||
width: auto;
|
Color coding for alerts in Alert History table
|
||||||
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;}
|
|
||||||
|
|
||||||
.alert-level-ok {
|
.alert-level-ok {
|
||||||
&, &:hover {color: $c-rainforest !important;}
|
&, &:hover {color: $c-rainforest !important;}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ $overlay-z: 100;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex: 0 0 $overlay-controls-height;
|
flex: 0 0 $overlay-controls-height;
|
||||||
width: calc(100% - #{($explorer-page-padding * 2)});
|
width: calc(100% - #{($page-wrapper-padding * 2)});
|
||||||
left: $explorer-page-padding;
|
left: $page-wrapper-padding;
|
||||||
border: 0;
|
border: 0;
|
||||||
background-color: $g2-kevlar;
|
background-color: $g2-kevlar;
|
||||||
border-radius: $radius $radius 0 0;
|
border-radius: $radius $radius 0 0;
|
||||||
|
@ -54,13 +54,16 @@ $overlay-z: 100;
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
|
width: auto;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $g13-mist;
|
color: $g13-mist;
|
||||||
margin: 0;
|
margin: 0 6px 0 0;
|
||||||
@include no-user-select;
|
@include no-user-select;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.overlay--graph-name {
|
.overlay--graph-name {
|
||||||
|
width: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
@ -78,28 +81,6 @@ $overlay-z: 100;
|
||||||
.overlay-controls .confirm-buttons {
|
.overlay-controls .confirm-buttons {
|
||||||
margin-left: 32px;
|
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 */
|
/* Graph editing in Dashboards is a little smaller so the dash can be seen in the background */
|
||||||
.overlay-technology .graph {
|
.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;
|
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 {
|
.modal {
|
||||||
form {
|
form {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -59,10 +44,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.form-group label,
|
|
||||||
.form-group label:hover {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generic Empty State
|
Generic Empty State
|
||||||
|
@ -71,67 +52,25 @@
|
||||||
.generic-empty-state {
|
.generic-empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: $g12-forge;
|
color: $g12-forge;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
|
|
||||||
|
h4, h5 {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-bottom: 11px;
|
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
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.loading-dots {
|
.loading-dots {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(0,0);
|
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;}
|
div:nth-child(3) {left: 100%; animation: refreshingSpinnerC 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) infinite;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Custom Tabs
|
Custom Tabs
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
@ -206,7 +144,9 @@
|
||||||
@include no-user-select();
|
@include no-user-select();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
br {
|
||||||
|
@include no-user-select();
|
||||||
|
}
|
||||||
|
|
||||||
.select-source-page {
|
.select-source-page {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
Loading…
Reference in New Issue