Merge branch 'master' into feature/influx-write
commit
365434783d
|
@ -4,13 +4,20 @@
|
|||
1. [#1364](https://github.com/influxdata/chronograf/pull/1364): Fix link to home when using the --basepath option
|
||||
1. [#1370](https://github.com/influxdata/chronograf/pull/1370): Remove notification to login outside of session timeout
|
||||
1. [#1376](https://github.com/influxdata/chronograf/pull/1376): Fix queries built in query builder with math functions in fields
|
||||
1. [#1399](https://github.com/influxdata/chronograf/pull/1399): User can no longer create a blank template variable by clicking outside a newly added one
|
||||
|
||||
### Features
|
||||
1. [#1382](https://github.com/influxdata/chronograf/pull/1382): Add line-protocol proxy for InfluxDB data sources
|
||||
1. [#1391](https://github.com/influxdata/chronograf/pull/1391): :dashboardTime: - Support cell-specific time ranges
|
||||
|
||||
### UI Improvements
|
||||
1. [#1378](https://github.com/influxdata/chronograf/pull/1378): Save query time range for dashboards
|
||||
1. [#1365](https://github.com/influxdata/chronograf/pull/1365): Show red indicator on Hosts Page for an offline host
|
||||
1. [#1373](https://github.com/influxdata/chronograf/pull/1373): Re-address dashboard cell stacking contexts
|
||||
1. [#602](https://github.com/influxdata/chronograf/pull/602): Normalize terminology in app
|
||||
1. [#1392](https://github.com/influxdata/chronograf/pull/1392): Overlays are now full screen
|
||||
1. [#1395](https://github.com/influxdata/chronograf/pull/1395): Change default global time range to past 1 hour
|
||||
1. [#1379](https://github.com/influxdata/chronograf/pull/1379): Re-add alert level colors on the alerts page
|
||||
|
||||
## v1.2.0-beta10 [2017-04-28]
|
||||
|
||||
|
|
|
@ -178,8 +178,7 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) {
|
|||
// If the condition has a time range we report back its duration
|
||||
if dur, ok := hasTimeRange(stmt.Condition); ok {
|
||||
qc.Range = &chronograf.DurationRange{
|
||||
Lower: shortDur(dur),
|
||||
Upper: "now",
|
||||
Lower: "now() - " + shortDur(dur),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,8 +59,7 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "15m",
|
||||
Upper: "now",
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -96,8 +95,7 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "0s",
|
||||
Upper: "now",
|
||||
Lower: "now() - 0s",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -121,8 +119,7 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "15m",
|
||||
Upper: "now",
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -190,8 +187,8 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
AreTagsAccepted: true,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "15m",
|
||||
Upper: "now",
|
||||
Lower: "now() - 15m",
|
||||
Upper: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -230,8 +227,7 @@ func TestConvert(t *testing.T) {
|
|||
},
|
||||
AreTagsAccepted: true,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "15m",
|
||||
Upper: "now",
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -284,8 +284,7 @@ func Test_newDashboardResponse(t *testing.T) {
|
|||
Tags: make(map[string][]string, 0),
|
||||
AreTagsAccepted: false,
|
||||
Range: &chronograf.DurationRange{
|
||||
Lower: "15m",
|
||||
Upper: "now",
|
||||
Lower: "now() - 15m",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import reducer from 'src/data_explorer/reducers/timeRange'
|
||||
|
||||
import {
|
||||
setTimeRange,
|
||||
} from 'src/data_explorer/actions/view'
|
||||
import {setTimeRange} from 'src/data_explorer/actions/view'
|
||||
|
||||
const noopAction = () => {
|
||||
return {type: 'NOOP'}
|
||||
|
@ -12,7 +10,7 @@ describe('DataExplorer.Reducers.TimeRange', () => {
|
|||
it('it sets the default timeRange', () => {
|
||||
const state = reducer(undefined, noopAction())
|
||||
const expected = {
|
||||
lower: 'now() - 15m',
|
||||
lower: 'now() - 1h',
|
||||
upper: null,
|
||||
}
|
||||
|
||||
|
|
|
@ -86,12 +86,13 @@ class CellEditorOverlay extends Component {
|
|||
|
||||
handleSaveCell() {
|
||||
const {queriesWorkingDraft, cellWorkingType, cellWorkingName} = this.state
|
||||
const {cell, timeRange} = this.props
|
||||
const {cell} = this.props
|
||||
|
||||
const newCell = _.cloneDeep(cell)
|
||||
newCell.name = cellWorkingName
|
||||
newCell.type = cellWorkingType
|
||||
newCell.queries = queriesWorkingDraft.map(q => {
|
||||
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
||||
const query = q.rawText || buildInfluxQLQuery(timeRange, q)
|
||||
const label = q.rawText ? '' : `${q.measurement}.${q.fields[0].field}`
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import omit from 'lodash/omit'
|
||||
|
||||
import TemplateControlBar from 'src/dashboards/components/TemplateControlBar'
|
||||
import LayoutRenderer from 'shared/components/LayoutRenderer'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
const Dashboard = ({
|
||||
source,
|
||||
timeRange,
|
||||
dashboard,
|
||||
onAddCell,
|
||||
onEditCell,
|
||||
|
@ -19,6 +16,7 @@ const Dashboard = ({
|
|||
onPositionChange,
|
||||
inPresentationMode,
|
||||
onOpenTemplateManager,
|
||||
templatesIncludingDashTime,
|
||||
onSummonOverlayTechnologies,
|
||||
onSelectTemplate,
|
||||
}) => {
|
||||
|
@ -26,8 +24,6 @@ const Dashboard = ({
|
|||
return null
|
||||
}
|
||||
|
||||
const {templates} = dashboard
|
||||
|
||||
const cells = dashboard.cells.map(cell => {
|
||||
const dashboardCell = {...cell}
|
||||
dashboardCell.queries = dashboardCell.queries.map(
|
||||
|
@ -50,43 +46,14 @@ const Dashboard = ({
|
|||
{'presentation-mode': inPresentationMode}
|
||||
)}
|
||||
>
|
||||
<div className="template-control-bar">
|
||||
<h1 className="template-control--heading">Template Variables</h1>
|
||||
{templates.map(({id, values, tempVar}) => {
|
||||
const items = values.map(value => ({...value, text: value.value}))
|
||||
const selectedItem = items.find(item => item.selected) || items[0]
|
||||
const selectedText = selectedItem && selectedItem.text
|
||||
|
||||
// TODO: change Dropdown to a MultiSelectDropdown, `selected` to
|
||||
// the full array, and [item] to all `selected` values when we update
|
||||
// this component to support multiple values
|
||||
return (
|
||||
<div key={id} className="template-control--dropdown">
|
||||
<Dropdown
|
||||
items={items}
|
||||
buttonSize="btn-xs"
|
||||
selected={selectedText || 'Loading...'}
|
||||
onChoose={item =>
|
||||
onSelectTemplate(id, [item].map(x => omit(x, 'text')))}
|
||||
/>
|
||||
<label className="template-control--label">
|
||||
{tempVar}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<button
|
||||
className="btn btn-primary btn-sm template-control--manage"
|
||||
onClick={onOpenTemplateManager}
|
||||
>
|
||||
<span className="icon cog-thick" />
|
||||
Manage
|
||||
</button>
|
||||
</div>
|
||||
<TemplateControlBar
|
||||
templates={dashboard.templates}
|
||||
onSelectTemplate={onSelectTemplate}
|
||||
onOpenTemplateManager={onOpenTemplateManager}
|
||||
/>
|
||||
{cells.length
|
||||
? <LayoutRenderer
|
||||
timeRange={timeRange}
|
||||
templates={templates}
|
||||
templates={templatesIncludingDashTime}
|
||||
cells={cells}
|
||||
autoRefresh={autoRefresh}
|
||||
source={source.links.proxy}
|
||||
|
@ -110,7 +77,27 @@ const Dashboard = ({
|
|||
const {arrayOf, bool, func, shape, string, number} = PropTypes
|
||||
|
||||
Dashboard.propTypes = {
|
||||
dashboard: shape({}).isRequired,
|
||||
dashboard: shape({
|
||||
templates: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
tempVar: string.isRequired,
|
||||
query: shape({
|
||||
db: string,
|
||||
rp: string,
|
||||
influxql: string,
|
||||
}),
|
||||
values: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
value: string.isRequired,
|
||||
selected: bool,
|
||||
})
|
||||
).isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
}).isRequired,
|
||||
templatesIncludingDashTime: arrayOf(shape()).isRequired,
|
||||
inPresentationMode: bool,
|
||||
onAddCell: func,
|
||||
onPositionChange: func,
|
||||
|
@ -128,24 +115,6 @@ Dashboard.propTypes = {
|
|||
timeRange: shape({}).isRequired,
|
||||
onOpenTemplateManager: func.isRequired,
|
||||
onSelectTemplate: func.isRequired,
|
||||
templates: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
tempVar: string.isRequired,
|
||||
query: shape({
|
||||
db: string,
|
||||
rp: string,
|
||||
influxql: string,
|
||||
}),
|
||||
values: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
value: string.isRequired,
|
||||
selected: bool,
|
||||
})
|
||||
).isRequired,
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
import omit from 'lodash/omit'
|
||||
|
||||
const TemplateControlBar = ({
|
||||
templates,
|
||||
onSelectTemplate,
|
||||
onOpenTemplateManager,
|
||||
}) => (
|
||||
<div className="template-control-bar">
|
||||
<div className="template-control--controls">
|
||||
{templates.map(({id, values, tempVar}) => {
|
||||
const items = values.map(value => ({...value, text: value.value}))
|
||||
const selectedItem = items.find(item => item.selected) || items[0]
|
||||
const selectedText = selectedItem && selectedItem.text
|
||||
|
||||
// TODO: change Dropdown to a MultiSelectDropdown, `selected` to
|
||||
// the full array, and [item] to all `selected` values when we update
|
||||
// this component to support multiple values
|
||||
return (
|
||||
<div key={id} className="template-control--dropdown">
|
||||
<Dropdown
|
||||
items={items}
|
||||
buttonSize="btn-xs"
|
||||
selected={selectedText || 'Loading...'}
|
||||
onChoose={item =>
|
||||
onSelectTemplate(id, [item].map(x => omit(x, 'text')))}
|
||||
/>
|
||||
<label className="template-control--label">
|
||||
{tempVar}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
className="btn btn-primary btn-sm template-control--manage"
|
||||
onClick={onOpenTemplateManager}
|
||||
>
|
||||
<span className="icon cog-thick" />
|
||||
Templates
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
TemplateControlBar.propTypes = {
|
||||
templates: arrayOf(
|
||||
shape({
|
||||
id: string.isRequired,
|
||||
values: arrayOf(
|
||||
shape({
|
||||
value: string.isRequired,
|
||||
})
|
||||
),
|
||||
tempVar: string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
onSelectTemplate: func.isRequired,
|
||||
onOpenTemplateManager: func.isRequired,
|
||||
}
|
||||
|
||||
export default TemplateControlBar
|
|
@ -301,7 +301,7 @@ class RowWrapper extends Component {
|
|||
}
|
||||
|
||||
handleClickOutside() {
|
||||
this.setState({isEditing: false})
|
||||
this.handleCancelEdit()
|
||||
}
|
||||
|
||||
handleStartEdit(name) {
|
||||
|
|
|
@ -226,6 +226,21 @@ class DashboardPage extends Component {
|
|||
} = this.props
|
||||
|
||||
const dashboard = dashboards.find(d => d.id === +dashboardID)
|
||||
const dashboardTime = {
|
||||
id: 'dashtime',
|
||||
tempVar: ':dashboardTime:',
|
||||
type: 'constant',
|
||||
values: [
|
||||
{
|
||||
value: timeRange.lower,
|
||||
type: 'constant',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const templatesIncludingDashTime = (dashboard &&
|
||||
dashboard.templates.concat(dashboardTime)) || []
|
||||
|
||||
const {selectedCell, isEditMode, isTemplating} = this.state
|
||||
|
||||
|
@ -245,7 +260,7 @@ class DashboardPage extends Component {
|
|||
{selectedCell
|
||||
? <CellEditorOverlay
|
||||
source={source}
|
||||
templates={dashboard.templates}
|
||||
templates={templatesIncludingDashTime}
|
||||
cell={selectedCell}
|
||||
timeRange={timeRange}
|
||||
autoRefresh={autoRefresh}
|
||||
|
@ -302,6 +317,7 @@ class DashboardPage extends Component {
|
|||
onRenameCell={this.handleRenameDashboardCell}
|
||||
onUpdateCell={this.handleUpdateDashboardCell}
|
||||
onOpenTemplateManager={this.handleOpenTemplateManager}
|
||||
templatesIncludingDashTime={templatesIncludingDashTime}
|
||||
onSummonOverlayTechnologies={this.handleSummonOverlayTechnologies}
|
||||
onSelectTemplate={this.handleSelectTemplate}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash'
|
||||
import timeRanges from 'hson!../../shared/data/timeRanges.hson'
|
||||
|
||||
const {lower, upper} = timeRanges[1]
|
||||
const {lower, upper} = timeRanges[2]
|
||||
|
||||
const initialState = {
|
||||
dashboards: [],
|
||||
|
|
|
@ -97,10 +97,12 @@ export function editRawText(queryId, rawText) {
|
|||
}
|
||||
}
|
||||
|
||||
export function setTimeRange(range) {
|
||||
export function setTimeRange(bounds) {
|
||||
return {
|
||||
type: 'SET_TIME_RANGE',
|
||||
payload: range,
|
||||
payload: {
|
||||
bounds,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import TagList from './TagList'
|
|||
import QueryEditor from './QueryEditor'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
const QueryBuilder = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -28,6 +28,7 @@ const QueryBuilder = React.createClass({
|
|||
tempVar: string.isRequired,
|
||||
})
|
||||
),
|
||||
isInDataExplorer: bool,
|
||||
actions: shape({
|
||||
chooseNamespace: func.isRequired,
|
||||
chooseMeasurement: func.isRequired,
|
||||
|
@ -79,7 +80,17 @@ const QueryBuilder = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {query, timeRange, templates} = this.props
|
||||
const {query, templates, isInDataExplorer} = this.props
|
||||
|
||||
// DE does not understand templating. :dashboardTime: is specific to dashboards
|
||||
let timeRange
|
||||
|
||||
if (isInDataExplorer) {
|
||||
timeRange = this.props.timeRange
|
||||
} else {
|
||||
timeRange = query.range || {upper: null, lower: ':dashboardTime:'}
|
||||
}
|
||||
|
||||
const q = query.rawText || buildInfluxQLQuery(timeRange, query) || ''
|
||||
|
||||
return (
|
||||
|
|
|
@ -66,6 +66,7 @@ class QueryEditor extends Component {
|
|||
|
||||
if (isTemplating) {
|
||||
switch (e.key) {
|
||||
case 'Tab':
|
||||
case 'ArrowRight':
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
|
@ -145,7 +146,8 @@ class QueryEditor extends Component {
|
|||
const {selectedTemplate} = this.state
|
||||
const value = this.editor.value
|
||||
const matches = value.match(TEMPLATE_MATCHER)
|
||||
if (matches) {
|
||||
|
||||
if (matches && !_.isEmpty(templates)) {
|
||||
// maintain cursor poition
|
||||
const start = this.editor.selectionStart
|
||||
const end = this.editor.selectionEnd
|
||||
|
|
|
@ -4,7 +4,7 @@ import QueryBuilder from './QueryBuilder'
|
|||
import QueryMakerTab from './QueryMakerTab'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
|
||||
const {arrayOf, func, node, number, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, node, number, shape, string} = PropTypes
|
||||
|
||||
const QueryMaker = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -23,6 +23,7 @@ const QueryMaker = React.createClass({
|
|||
tempVar: string.isRequired,
|
||||
})
|
||||
),
|
||||
isInDataExplorer: bool,
|
||||
actions: shape({
|
||||
chooseNamespace: func.isRequired,
|
||||
chooseMeasurement: func.isRequired,
|
||||
|
@ -74,7 +75,7 @@ const QueryMaker = React.createClass({
|
|||
},
|
||||
|
||||
renderQueryBuilder() {
|
||||
const {timeRange, actions, source, templates} = this.props
|
||||
const {timeRange, actions, source, templates, isInDataExplorer} = this.props
|
||||
const query = this.getActiveQuery()
|
||||
|
||||
if (!query) {
|
||||
|
@ -101,6 +102,7 @@ const QueryMaker = React.createClass({
|
|||
query={query}
|
||||
actions={actions}
|
||||
onAddQuery={this.handleAddQuery}
|
||||
isInDataExplorer={isInDataExplorer}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@ const VisView = ({
|
|||
heightPixels,
|
||||
editQueryStatus,
|
||||
activeQueryIndex,
|
||||
isInDataExplorer,
|
||||
}) => {
|
||||
const activeQuery = queries[activeQueryIndex]
|
||||
const defaultQuery = queries[0]
|
||||
|
@ -56,7 +57,7 @@ const VisView = ({
|
|||
autoRefresh={autoRefresh}
|
||||
templates={templates}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
isInDataExplorer={true}
|
||||
isInDataExplorer={isInDataExplorer}
|
||||
showSingleStat={cellType === 'line-plus-single-stat'}
|
||||
displayOptions={displayOptions}
|
||||
editQueryStatus={editQueryStatus}
|
||||
|
@ -64,7 +65,7 @@ const VisView = ({
|
|||
)
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
|
||||
VisView.propTypes = {
|
||||
view: string.isRequired,
|
||||
|
@ -75,6 +76,7 @@ VisView.propTypes = {
|
|||
heightPixels: number,
|
||||
editQueryStatus: func.isRequired,
|
||||
activeQueryIndex: number,
|
||||
isInDataExplorer: bool,
|
||||
}
|
||||
|
||||
export default VisView
|
||||
|
|
|
@ -6,7 +6,7 @@ import VisView from 'src/data_explorer/components/VisView'
|
|||
import {GRAPH, TABLE} from 'src/shared/constants'
|
||||
import _ from 'lodash'
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
const META_QUERY_REGEX = /^show/i
|
||||
|
||||
const Visualization = React.createClass({
|
||||
|
@ -15,6 +15,7 @@ const Visualization = React.createClass({
|
|||
cellType: string,
|
||||
autoRefresh: number.isRequired,
|
||||
templates: arrayOf(shape()),
|
||||
isInDataExplorer: bool,
|
||||
timeRange: shape({
|
||||
upper: string,
|
||||
lower: string,
|
||||
|
@ -86,6 +87,7 @@ const Visualization = React.createClass({
|
|||
queryConfigs,
|
||||
editQueryStatus,
|
||||
activeQueryIndex,
|
||||
isInDataExplorer,
|
||||
} = this.props
|
||||
const {source: {links: {proxy}}} = this.context
|
||||
const {view} = this.state
|
||||
|
@ -121,6 +123,7 @@ const Visualization = React.createClass({
|
|||
heightPixels={heightPixels}
|
||||
editQueryStatus={editQueryStatus}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
isInDataExplorer={isInDataExplorer}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -97,12 +97,14 @@ const DataExplorer = React.createClass({
|
|||
actions={queryConfigActions}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
isInDataExplorer={true}
|
||||
setActiveQueryIndex={this.handleSetActiveQueryIndex}
|
||||
onDeleteQuery={this.handleDeleteQuery}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
/>
|
||||
<ResizeBottom>
|
||||
<Visualization
|
||||
isInDataExplorer={true}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
queryConfigs={queryConfigs}
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
import timeRanges from 'hson!../../shared/data/timeRanges.hson'
|
||||
|
||||
const initialLower = timeRanges[1].lower
|
||||
const initialUpper = timeRanges[1].upper
|
||||
const {lower, upper} = timeRanges[2]
|
||||
|
||||
const initialState = {
|
||||
upper: initialUpper,
|
||||
lower: initialLower,
|
||||
upper,
|
||||
lower,
|
||||
}
|
||||
|
||||
export default function timeRange(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case 'SET_TIME_RANGE': {
|
||||
const {upper, lower} = action.payload
|
||||
const newState = {
|
||||
upper,
|
||||
lower,
|
||||
}
|
||||
const {bounds} = action.payload
|
||||
|
||||
return {...state, ...newState}
|
||||
return {...state, ...bounds}
|
||||
}
|
||||
}
|
||||
return state
|
||||
|
|
|
@ -129,7 +129,7 @@ const HostsTable = React.createClass({
|
|||
onClick={() => this.updateSort('name')}
|
||||
className={this.sortableClasses('name')}
|
||||
>
|
||||
Hostname
|
||||
Host
|
||||
</th>
|
||||
<th
|
||||
onClick={() => this.updateSort('deltaUptime')}
|
||||
|
@ -262,7 +262,7 @@ const SearchBar = React.createClass({
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter by Hostname..."
|
||||
placeholder="Filter by Host..."
|
||||
ref="searchInput"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
|
|
@ -45,12 +45,12 @@ export const HostPage = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState() {
|
||||
const fifteenMinutesIndex = 1
|
||||
const timeRange = timeRanges[2]
|
||||
|
||||
return {
|
||||
layouts: [],
|
||||
hosts: [],
|
||||
timeRange: timeRanges[fifteenMinutesIndex],
|
||||
timeRange,
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ export const KapacitorRule = React.createClass({
|
|||
}
|
||||
|
||||
if (this.thresholdValueEmpty() || this.relativeValueEmpty()) {
|
||||
return 'Please enter a value in the Values section'
|
||||
return 'Please enter a value in the Rule Conditions section'
|
||||
}
|
||||
|
||||
return ''
|
||||
|
|
|
@ -28,11 +28,11 @@ const KapacitorRules = ({
|
|||
</PageContents>
|
||||
)
|
||||
}
|
||||
|
||||
const tableHeader = rules.length === 1 ? '1 Alert Rule' : `${rules.length} Alert Rules`
|
||||
return (
|
||||
<PageContents source={source}>
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">Alert Rules</h2>
|
||||
<h2 className="panel-title">{tableHeader}</h2>
|
||||
<Link
|
||||
to={`/sources/${source.id}/alert-rules/new`}
|
||||
className="btn btn-sm btn-primary"
|
||||
|
@ -55,7 +55,7 @@ const PageContents = ({children, source}) => (
|
|||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Kapacitor Rules</h1>
|
||||
<h1 className="page-header__title">Alert Rules</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<SourceIndicator sourceName={source && source.name} />
|
||||
|
|
|
@ -8,7 +8,7 @@ const KapacitorRulesTable = ({source, rules, onDelete, onChangeRuleStatus}) => {
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Trigger</th>
|
||||
<th>Rule Type</th>
|
||||
<th>Message</th>
|
||||
<th>Alerts</th>
|
||||
<th className="text-center">Enabled</th>
|
||||
|
|
|
@ -23,7 +23,7 @@ export const ValuesSection = React.createClass({
|
|||
|
||||
return (
|
||||
<div className="kapacitor-rule-section">
|
||||
<h3 className="rule-section-heading">Values</h3>
|
||||
<h3 className="rule-section-heading">Rule Conditions</h3>
|
||||
<div className="rule-section-body">
|
||||
<Tabs initialIndex={initialIndex} onSelect={this.handleChooseTrigger}>
|
||||
<TabList isKapacitorTabs="true">
|
||||
|
|
|
@ -21,7 +21,6 @@ const AutoRefresh = ComposedComponent => {
|
|||
templates: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
label: string.isRequired,
|
||||
tempVar: string.isRequired,
|
||||
query: shape({
|
||||
db: string,
|
||||
|
|
|
@ -20,7 +20,7 @@ export const LayoutRenderer = React.createClass({
|
|||
autoRefresh: number.isRequired,
|
||||
timeRange: shape({
|
||||
lower: string.isRequired,
|
||||
}).isRequired,
|
||||
}),
|
||||
cells: arrayOf(
|
||||
shape({
|
||||
queries: arrayOf(
|
||||
|
@ -114,7 +114,6 @@ export const LayoutRenderer = React.createClass({
|
|||
|
||||
generateVisualizations() {
|
||||
const {
|
||||
timeRange,
|
||||
source,
|
||||
cells,
|
||||
onEditCell,
|
||||
|
@ -133,7 +132,8 @@ export const LayoutRenderer = React.createClass({
|
|||
// on a stable query representation.
|
||||
let queryText
|
||||
if (query.queryConfig) {
|
||||
const {queryConfig: {rawText}} = query
|
||||
const {queryConfig: {rawText, range}} = query
|
||||
const timeRange = range || {upper: null, lower: ':dashboardTime:'}
|
||||
queryText =
|
||||
rawText || buildInfluxQLQuery(timeRange, query.queryConfig)
|
||||
} else {
|
||||
|
|
|
@ -65,7 +65,7 @@ const SideNav = React.createClass({
|
|||
Alert History
|
||||
</NavListItem>
|
||||
<NavListItem link={`${sourcePrefix}/alert-rules`}>
|
||||
Kapacitor Rules
|
||||
Alert Rules
|
||||
</NavListItem>
|
||||
</NavBlock>
|
||||
<NavBlock icon="crown2" link={`${sourcePrefix}/admin`}>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
@import 'layout/page';
|
||||
@import 'layout/page-header';
|
||||
@import 'layout/sidebar';
|
||||
@import 'layout/overlay';
|
||||
|
||||
// Components
|
||||
@import 'components/confirm-buttons';
|
||||
|
|
|
@ -6,15 +6,18 @@
|
|||
*/
|
||||
|
||||
$template-control--margin: 2px;
|
||||
$template-control--min-height: 52px;
|
||||
|
||||
.template-control-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
padding: $template-control--margin;
|
||||
@extend .cell-shell;
|
||||
background-color: $g0-obsidian;
|
||||
min-height: $template-control--min-height;
|
||||
}
|
||||
.template-control--heading {
|
||||
font-size: 16px;
|
||||
|
@ -25,7 +28,12 @@ $template-control--margin: 2px;
|
|||
white-space: nowrap;
|
||||
}
|
||||
.template-control--manage {
|
||||
margin: $template-control--margin 8px;
|
||||
margin: 7px 8px;
|
||||
}
|
||||
.template-control--controls {
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.template-control--dropdown {
|
||||
flex: 0 1 auto;
|
||||
|
@ -60,7 +68,7 @@ $template-control--margin: 2px;
|
|||
&:hover {@include gradient-h($c-comet,$c-pool);}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.template-control--label {
|
||||
@include no-user-select();
|
||||
order: 1;
|
||||
|
@ -73,4 +81,4 @@ $template-control--margin: 2px;
|
|||
line-height: 18px;
|
||||
border-radius: $radius-small $radius-small 0 0;
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Manages Overlays
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.page, .sidebar {
|
||||
position: relative;
|
||||
}
|
||||
.page {
|
||||
z-index: 2;
|
||||
}
|
||||
.sidebar {
|
||||
z-index: 1;
|
||||
|
||||
// Ensures that sidebar menus appear above the rest of the app on hover
|
||||
&:hover {z-index: 2;}
|
||||
&:hover + .page {z-index: 1;}
|
||||
}
|
||||
|
||||
// Make Overlay Technology full screen
|
||||
.overlay-technology {
|
||||
left: -($sidebar-width) !important;
|
||||
}
|
|
@ -13,9 +13,7 @@
|
|||
color: $g17-whisper;
|
||||
}
|
||||
.page {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-contents {
|
||||
position: absolute;
|
||||
|
@ -35,15 +33,6 @@
|
|||
&--purple-scrollbar {
|
||||
@include custom-scrollbar($g2-kevlar,$c-comet);
|
||||
}
|
||||
|
||||
&.presentation-mode {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
|
||||
.dashboard {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.container-fluid {
|
||||
padding: ($chronograf-page-header-height / 2) $page-wrapper-padding ($chronograf-page-header-height / 2) $page-wrapper-padding;
|
||||
|
|
|
@ -41,6 +41,18 @@ $dash-graph-options-arrow: 8px;
|
|||
}
|
||||
|
||||
.dashboard {
|
||||
&.container-fluid.full-width.page-contents {
|
||||
padding-top: 8px;
|
||||
}
|
||||
&.presentation-mode {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
|
||||
.template-control--manage {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.react-grid-item {
|
||||
@extend .cell-shell;
|
||||
}
|
||||
|
|
|
@ -462,3 +462,13 @@ input.size-49 {width: 49px;}
|
|||
.dropdown.size-106 .dropdown-toggle {width: 106px;}
|
||||
.dropdown.size-66 .dropdown-toggle {width: 66px;}
|
||||
.dropdown.size-49 .dropdown-toggle {width: 49px;}
|
||||
|
||||
.alert-level-ok {
|
||||
&, &:hover {color: $c-rainforest !important;}
|
||||
}
|
||||
.alert-level-warning {
|
||||
&, &:hover {color: $c-pineapple !important;}
|
||||
}
|
||||
.alert-level-critical {
|
||||
&, &:hover {color: $c-dreamsicle !important;}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ $overlay-z: 100;
|
|||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: $overlay-z;
|
||||
padding: 0 30px;
|
||||
|
|
Loading…
Reference in New Issue