Typescriptify Status Page and children
parent
9b51924921
commit
5d7958988f
|
@ -48,7 +48,7 @@ interface State {
|
|||
|
||||
@ErrorHandling
|
||||
export class AllUsersPage extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||
import {TimeRange} from '../../types'
|
||||
import {TimeRange} from 'src/types'
|
||||
|
||||
export const getAlerts = (
|
||||
source: string,
|
||||
|
|
|
@ -3,15 +3,38 @@ import {noop} from 'src/shared/actions/app'
|
|||
import _ from 'lodash'
|
||||
|
||||
import {errorThrown} from 'src/shared/actions/errors'
|
||||
import {TimeSeriesResponse} from 'src/types/series'
|
||||
|
||||
export const handleLoading = (query, editQueryStatus) => {
|
||||
interface Query {
|
||||
host: string | string[]
|
||||
text: string
|
||||
id: string
|
||||
database?: string
|
||||
db?: string
|
||||
rp?: string
|
||||
}
|
||||
|
||||
interface Payload {
|
||||
source: string
|
||||
query: Query
|
||||
tempVars: any[]
|
||||
db?: string
|
||||
rp?: string
|
||||
resolution?: number
|
||||
}
|
||||
|
||||
export const handleLoading = (query: Query, editQueryStatus) => {
|
||||
editQueryStatus(query.id, {
|
||||
loading: true,
|
||||
})
|
||||
}
|
||||
|
||||
// {results: [{}]}
|
||||
export const handleSuccess = (data, query, editQueryStatus) => {
|
||||
export const handleSuccess = (
|
||||
data: TimeSeriesResponse,
|
||||
query: Query,
|
||||
editQueryStatus
|
||||
) => {
|
||||
const {results} = data
|
||||
const error = _.get(results, ['0', 'error'], false)
|
||||
const series = _.get(results, ['0', 'series'], false)
|
||||
|
@ -51,24 +74,6 @@ export const handleError = (error, query, editQueryStatus) => {
|
|||
})
|
||||
}
|
||||
|
||||
interface Query {
|
||||
host: string | string[]
|
||||
text: string
|
||||
id: string
|
||||
database?: string
|
||||
db?: string
|
||||
rp?: string
|
||||
}
|
||||
|
||||
interface Payload {
|
||||
source: string
|
||||
query: Query
|
||||
tempVars: any[]
|
||||
db?: string
|
||||
rp?: string
|
||||
resolution?: number
|
||||
}
|
||||
|
||||
export const fetchTimeSeriesAsync = async (
|
||||
{source, db, rp, query, tempVars, resolution}: Payload,
|
||||
editQueryStatus = noop
|
||||
|
|
|
@ -175,8 +175,10 @@ class Dygraph extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.dygraph.destroy()
|
||||
delete this.dygraph
|
||||
if (this.dygraph) {
|
||||
this.dygraph.destroy()
|
||||
delete this.dygraph
|
||||
}
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(nextProps: Props, nextState: State) {
|
||||
|
|
|
@ -427,7 +427,23 @@ export const DYGRAPH_CONTAINER_H_MARGIN = 16
|
|||
export const DYGRAPH_CONTAINER_V_MARGIN = 8
|
||||
export const DYGRAPH_CONTAINER_XLABEL_MARGIN = 20
|
||||
|
||||
export const DEFAULT_SOURCE_LINKS = {
|
||||
self: '',
|
||||
kapacitors: '',
|
||||
proxy: '',
|
||||
queries: '',
|
||||
write: '',
|
||||
permissions: '',
|
||||
users: '',
|
||||
roles: '',
|
||||
databases: '',
|
||||
annotations: '',
|
||||
health: '',
|
||||
services: '',
|
||||
}
|
||||
|
||||
export const DEFAULT_SOURCE = {
|
||||
id: '',
|
||||
url: 'http://localhost:8086',
|
||||
name: 'Influx 1',
|
||||
username: '',
|
||||
|
@ -436,6 +452,11 @@ export const DEFAULT_SOURCE = {
|
|||
telegraf: 'telegraf',
|
||||
insecureSkipVerify: false,
|
||||
metaUrl: '',
|
||||
organization: '',
|
||||
role: '',
|
||||
defaultRP: '',
|
||||
links: DEFAULT_SOURCE_LINKS,
|
||||
type: '',
|
||||
}
|
||||
|
||||
export const defaultIntervalValue = '333'
|
||||
|
|
|
@ -6,6 +6,8 @@ interface TimeRangeOption extends TimeRange {
|
|||
menuOption: string
|
||||
}
|
||||
|
||||
const nowminus30d = 'now() - 30d'
|
||||
|
||||
export const timeRanges: TimeRangeOption[] = [
|
||||
{
|
||||
defaultGroupBy: '10s',
|
||||
|
@ -75,7 +77,7 @@ export const timeRanges: TimeRangeOption[] = [
|
|||
defaultGroupBy: '6h',
|
||||
seconds: 2592000,
|
||||
inputValue: 'Past 30d',
|
||||
lower: 'now() - 30d',
|
||||
lower: nowminus30d,
|
||||
upper: null,
|
||||
menuOption: 'Past 30d',
|
||||
},
|
||||
|
@ -89,3 +91,7 @@ export const defaultTimeRange = {
|
|||
seconds: 900,
|
||||
format: FORMAT_INFLUXQL,
|
||||
}
|
||||
|
||||
export const STATUS_PAGE_TIME_RANGE = timeRanges.find(
|
||||
tr => tr.lower === nowminus30d
|
||||
)
|
||||
|
|
|
@ -14,12 +14,12 @@ import {
|
|||
notifySourceDeleteFailed,
|
||||
} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Source, NotificationFunc} from 'src/types'
|
||||
import {Source, Notification} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
source: Source
|
||||
sources: Source[]
|
||||
notify: (n: NotificationFunc) => void
|
||||
notify: (n: Notification) => void
|
||||
deleteKapacitor: actions.DeleteKapacitorAsync
|
||||
fetchKapacitors: actions.FetchKapacitorsAsync
|
||||
removeAndLoadSources: actions.RemoveAndLoadSources
|
||||
|
|
|
@ -1,278 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {withRouter} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
import {getSource} from 'shared/apis'
|
||||
import {createSource, updateSource} from 'shared/apis'
|
||||
import {
|
||||
addSource as addSourceAction,
|
||||
updateSource as updateSourceAction,
|
||||
} from 'shared/actions/sources'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import Notifications from 'shared/components/Notifications'
|
||||
import SourceForm from 'src/sources/components/SourceForm'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import {DEFAULT_SOURCE} from 'shared/constants'
|
||||
const initialPath = '/sources/new'
|
||||
|
||||
import {
|
||||
notifyErrorConnectingToSource,
|
||||
notifySourceCreationSucceeded,
|
||||
notifySourceCreationFailed,
|
||||
notifySourceUdpated,
|
||||
notifySourceUdpateFailed,
|
||||
} from 'shared/copy/notifications'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class SourcePage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
source: DEFAULT_SOURCE,
|
||||
editMode: props.params.id !== undefined,
|
||||
isInitialSource: props.router.location.pathname === initialPath,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {editMode} = this.state
|
||||
const {params, notify} = this.props
|
||||
|
||||
if (!editMode) {
|
||||
return this.setState({isLoading: false})
|
||||
}
|
||||
|
||||
getSource(params.id)
|
||||
.then(({data: source}) => {
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...source},
|
||||
isLoading: false,
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifyErrorConnectingToSource(this._parseError(error)))
|
||||
this.setState({isLoading: false})
|
||||
})
|
||||
}
|
||||
|
||||
handleInputChange = e => {
|
||||
let val = e.target.value
|
||||
const name = e.target.name
|
||||
|
||||
if (e.target.type === 'checkbox') {
|
||||
val = e.target.checked
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const source = {
|
||||
...prevState.source,
|
||||
[name]: val,
|
||||
}
|
||||
|
||||
return {...prevState, source}
|
||||
})
|
||||
}
|
||||
|
||||
handleBlurSourceURL = () => {
|
||||
const {source, editMode} = this.state
|
||||
if (editMode) {
|
||||
this.setState(this._normalizeSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (!source.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._createSourceOnBlur)
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault()
|
||||
const {isCreated, editMode} = this.state
|
||||
const isNewSource = !editMode
|
||||
|
||||
if (!isCreated && isNewSource) {
|
||||
return this.setState(this._normalizeSource, this._createSource)
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._updateSource)
|
||||
}
|
||||
|
||||
gotoPurgatory = () => {
|
||||
const {router} = this.props
|
||||
router.push('/purgatory')
|
||||
}
|
||||
|
||||
_normalizeSource({source}) {
|
||||
const url = source.url.trim()
|
||||
if (source.url.startsWith('http')) {
|
||||
return {source: {...source, url}}
|
||||
}
|
||||
return {source: {...source, url: `http://${url}`}}
|
||||
}
|
||||
|
||||
_createSourceOnBlur = () => {
|
||||
const {source} = this.state
|
||||
// if there is a type on source it has already been created
|
||||
if (source.type) {
|
||||
return
|
||||
}
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...sourceFromServer},
|
||||
isCreated: true,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
// dont want to flash this until they submit
|
||||
const error = this._parseError(err)
|
||||
console.error('Error creating InfluxDB connection: ', error)
|
||||
})
|
||||
}
|
||||
|
||||
_createSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceCreationSucceeded(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceCreationFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_updateSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
updateSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.updateSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceUdpated(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceUdpateFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_redirect = source => {
|
||||
const {isInitialSource} = this.state
|
||||
const {params, router} = this.props
|
||||
|
||||
if (isInitialSource) {
|
||||
return this._redirectToApp(source)
|
||||
}
|
||||
|
||||
router.push(`/sources/${params.sourceID}/manage-sources`)
|
||||
}
|
||||
|
||||
_redirectToApp = source => {
|
||||
const {location, router} = this.props
|
||||
const {redirectPath} = location.query
|
||||
|
||||
if (!redirectPath) {
|
||||
return router.push(`/sources/${source.id}/hosts`)
|
||||
}
|
||||
|
||||
const fixedPath = redirectPath.replace(
|
||||
/\/sources\/[^/]*/,
|
||||
`/sources/${source.id}`
|
||||
)
|
||||
return router.push(fixedPath)
|
||||
}
|
||||
|
||||
_parseError = error => {
|
||||
return _.get(error, ['data', 'message'], error)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isLoading, source, editMode, isInitialSource} = this.state
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="page-spinner" />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${isInitialSource ? '' : 'page'}`}>
|
||||
<Notifications />
|
||||
<div className="page-header">
|
||||
<div className="page-header__container page-header__source-page">
|
||||
<div className="page-header__col-md-8">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">
|
||||
{editMode
|
||||
? 'Configure InfluxDB Connection'
|
||||
: 'Add a New InfluxDB Connection'}
|
||||
</h1>
|
||||
</div>
|
||||
{isInitialSource ? null : (
|
||||
<div className="page-header__right">
|
||||
<SourceIndicator />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel">
|
||||
<SourceForm
|
||||
source={source}
|
||||
editMode={editMode}
|
||||
onInputChange={this.handleInputChange}
|
||||
onSubmit={this.handleSubmit}
|
||||
onBlurSourceURL={this.handleBlurSourceURL}
|
||||
isInitialSource={isInitialSource}
|
||||
gotoPurgatory={this.gotoPurgatory}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
SourcePage.propTypes = {
|
||||
params: shape({
|
||||
id: string,
|
||||
sourceID: string,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: shape({
|
||||
query: shape({
|
||||
redirectPath: string,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
notify: func.isRequired,
|
||||
addSource: func.isRequired,
|
||||
updateSource: func.isRequired,
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
addSource: bindActionCreators(addSourceAction, dispatch),
|
||||
updateSource: bindActionCreators(updateSourceAction, dispatch),
|
||||
})
|
||||
export default connect(null, mapDispatchToProps)(withRouter(SourcePage))
|
|
@ -1,7 +1,6 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {withRouter} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
import React, {Component, ChangeEvent, FormEvent} from 'react'
|
||||
import {withRouter, InjectedRouter} from 'react-router'
|
||||
import {Location} from 'history'
|
||||
import {getSource} from 'src/shared/apis'
|
||||
import {createSource, updateSource} from 'src/shared/apis'
|
||||
import {
|
||||
|
@ -27,45 +26,47 @@ import {
|
|||
notifySourceUdpateFailed,
|
||||
} from 'src/shared/copy/notifications'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {Source} from 'src/types'
|
||||
import {Source, Notification, NotificationFunc} from 'src/types'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
interface Params {
|
||||
id: string
|
||||
hash: string
|
||||
sourceID: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
params: shape({
|
||||
id: string,
|
||||
sourceID: string,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: shape({
|
||||
query: shape({
|
||||
redirectPath: string,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
notify: func.isRequired,
|
||||
addSource: func.isRequired,
|
||||
updateSource: func.isRequired,}
|
||||
location: Location
|
||||
router: InjectedRouter
|
||||
params: Params
|
||||
notify: (notification: Notification | NotificationFunc) => void
|
||||
addSource: (s: Source) => void
|
||||
updateSource: (s: Source) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
isLoading: boolean
|
||||
isCreated: boolean
|
||||
source: Source
|
||||
editMode: boolean
|
||||
isInitialSource: boolean}
|
||||
isInitialSource: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class SourcePage extends Component<Props, State> {
|
||||
constructor(props:Props) {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
isCreated: false,
|
||||
source: DEFAULT_SOURCE,
|
||||
editMode: props.params.id !== undefined,
|
||||
isInitialSource: props.router.location.pathname === initialPath,
|
||||
isInitialSource: props.location.pathname === initialPath,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
const {editMode} = this.state
|
||||
const {params, notify} = this.props
|
||||
|
||||
|
@ -81,148 +82,12 @@ class SourcePage extends Component<Props, State> {
|
|||
})
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifyErrorConnectingToSource(this._parseError(error)))
|
||||
notify(notifyErrorConnectingToSource(this.parseError(error)))
|
||||
this.setState({isLoading: false})
|
||||
})
|
||||
}
|
||||
|
||||
handleInputChange = e => {
|
||||
let val = e.target.value
|
||||
const name = e.target.name
|
||||
|
||||
if (e.target.type === 'checkbox') {
|
||||
val = e.target.checked
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const source = {
|
||||
...prevState.source,
|
||||
[name]: val,
|
||||
}
|
||||
|
||||
return {...prevState, source}
|
||||
})
|
||||
}
|
||||
|
||||
handleBlurSourceURL = () => {
|
||||
const {source, editMode} = this.state
|
||||
if (editMode) {
|
||||
this.setState(this._normalizeSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (!source.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._createSourceOnBlur)
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault()
|
||||
const {isCreated, editMode} = this.state
|
||||
const isNewSource = !editMode
|
||||
|
||||
if (!isCreated && isNewSource) {
|
||||
return this.setState(this._normalizeSource, this._createSource)
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._updateSource)
|
||||
}
|
||||
|
||||
gotoPurgatory = () => {
|
||||
const {router} = this.props
|
||||
router.push('/purgatory')
|
||||
}
|
||||
|
||||
_normalizeSource({source}) {
|
||||
const url = source.url.trim()
|
||||
if (source.url.startsWith('http')) {
|
||||
return {source: {...source, url}}
|
||||
}
|
||||
return {source: {...source, url: `http://${url}`}}
|
||||
}
|
||||
|
||||
_createSourceOnBlur = () => {
|
||||
const {source} = this.state
|
||||
// if there is a type on source it has already been created
|
||||
if (source.type) {
|
||||
return
|
||||
}
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...sourceFromServer},
|
||||
isCreated: true,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
// dont want to flash this until they submit
|
||||
const error = this._parseError(err)
|
||||
console.error('Error creating InfluxDB connection: ', error)
|
||||
})
|
||||
}
|
||||
|
||||
_createSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceCreationSucceeded(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceCreationFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_updateSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
updateSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.updateSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceUdpated(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceUdpateFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_redirect = source => {
|
||||
const {isInitialSource} = this.state
|
||||
const {params, router} = this.props
|
||||
|
||||
if (isInitialSource) {
|
||||
return this._redirectToApp(source)
|
||||
}
|
||||
|
||||
router.push(`/sources/${params.sourceID}/manage-sources`)
|
||||
}
|
||||
|
||||
_redirectToApp = source => {
|
||||
const {location, router} = this.props
|
||||
const {redirectPath} = location.query
|
||||
|
||||
if (!redirectPath) {
|
||||
return router.push(`/sources/${source.id}/hosts`)
|
||||
}
|
||||
|
||||
const fixedPath = redirectPath.replace(
|
||||
/\/sources\/[^/]*/,
|
||||
`/sources/${source.id}`
|
||||
)
|
||||
return router.push(fixedPath)
|
||||
}
|
||||
|
||||
_parseError = error => {
|
||||
return _.get(error, ['data', 'message'], error)
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const {isLoading, source, editMode, isInitialSource} = this.state
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -272,26 +137,143 @@ class SourcePage extends Component<Props, State> {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
private handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
let val: string | boolean = e.target.value
|
||||
const name = e.target.name
|
||||
|
||||
SourcePage.propTypes = {
|
||||
params: shape({
|
||||
id: string,
|
||||
sourceID: string,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: shape({
|
||||
query: shape({
|
||||
redirectPath: string,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
notify: func.isRequired,
|
||||
addSource: func.isRequired,
|
||||
updateSource: func.isRequired,
|
||||
if (e.target.type === 'checkbox') {
|
||||
val = e.target.checked
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const source = {
|
||||
...prevState.source,
|
||||
[name]: val,
|
||||
}
|
||||
|
||||
return {...prevState, source}
|
||||
})
|
||||
}
|
||||
|
||||
private handleBlurSourceURL = () => {
|
||||
const {source, editMode} = this.state
|
||||
if (editMode) {
|
||||
this.setState(this.normalizeSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (!source.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(this.normalizeSource, this.createSourceOnBlur)
|
||||
}
|
||||
|
||||
private handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
const {isCreated, editMode} = this.state
|
||||
const isNewSource = !editMode
|
||||
|
||||
if (!isCreated && isNewSource) {
|
||||
return this.setState(this.normalizeSource, this.createSource)
|
||||
}
|
||||
|
||||
this.setState(this.normalizeSource, this.updateSource)
|
||||
}
|
||||
|
||||
private gotoPurgatory = () => {
|
||||
const {router} = this.props
|
||||
router.push('/purgatory')
|
||||
}
|
||||
|
||||
private normalizeSource() {
|
||||
const {source} = this.state
|
||||
const url = source.url.trim()
|
||||
if (source.url.startsWith('http')) {
|
||||
return {source: {...source, url}}
|
||||
}
|
||||
return {source: {...source, url: `http://${url}`}}
|
||||
}
|
||||
|
||||
private createSourceOnBlur = () => {
|
||||
const {source} = this.state
|
||||
// if there is a type on source it has already been created
|
||||
if (source.type) {
|
||||
return
|
||||
}
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...sourceFromServer},
|
||||
isCreated: true,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
// dont want to flash this until they submit
|
||||
const error = this.parseError(err)
|
||||
console.error('Error creating InfluxDB connection: ', error)
|
||||
})
|
||||
}
|
||||
|
||||
private createSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.redirect(sourceFromServer)
|
||||
notify(notifySourceCreationSucceeded(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceCreationFailed(source.name, this.parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
private updateSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
updateSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.updateSource(sourceFromServer)
|
||||
this.redirect(sourceFromServer)
|
||||
notify(notifySourceUdpated(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceUdpateFailed(source.name, this.parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
private redirect = (source: Source) => {
|
||||
const {isInitialSource} = this.state
|
||||
const {params, router} = this.props
|
||||
|
||||
if (isInitialSource) {
|
||||
return this.redirectToApp(source)
|
||||
}
|
||||
|
||||
router.push(`/sources/${params.sourceID}/manage-sources`)
|
||||
}
|
||||
|
||||
private redirectToApp = (source: Source) => {
|
||||
const {location, router} = this.props
|
||||
const {redirectPath} = location.query
|
||||
|
||||
if (!redirectPath) {
|
||||
return router.push(`/sources/${source.id}/hosts`)
|
||||
}
|
||||
|
||||
const fixedPath = redirectPath.replace(
|
||||
/\/sources\/[^/]*/,
|
||||
`/sources/${source.id}`
|
||||
)
|
||||
return router.push(fixedPath)
|
||||
}
|
||||
|
||||
private parseError = error => {
|
||||
return getDeep<string>(error, 'data.message', '')
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
@ -299,4 +281,4 @@ const mapDispatchToProps = dispatch => ({
|
|||
addSource: bindActionCreators(addSourceAction, dispatch),
|
||||
updateSource: bindActionCreators(updateSourceAction, dispatch),
|
||||
})
|
||||
export default connect(null, mapDispatchToProps)(withRouter(SourcePage))
|
||||
export default withRouter(connect(null, mapDispatchToProps)(SourcePage))
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// he is a library for safely encoding and decoding HTML Entities
|
||||
import he from 'he'
|
||||
|
||||
import {fetchJSONFeed as fetchJSONFeedAJAX} from 'src/status/apis'
|
||||
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {notifyJSONFeedFailed} from 'src/shared/copy/notifications'
|
||||
|
||||
import * as actionTypes from 'src/status/constants/actionTypes'
|
||||
|
||||
const fetchJSONFeedRequested = () => ({
|
||||
type: actionTypes.FETCH_JSON_FEED_REQUESTED,
|
||||
})
|
||||
|
||||
const fetchJSONFeedCompleted = data => ({
|
||||
type: actionTypes.FETCH_JSON_FEED_COMPLETED,
|
||||
payload: {data},
|
||||
})
|
||||
|
||||
const fetchJSONFeedFailed = () => ({
|
||||
type: actionTypes.FETCH_JSON_FEED_FAILED,
|
||||
})
|
||||
|
||||
export const fetchJSONFeedAsync = url => async dispatch => {
|
||||
dispatch(fetchJSONFeedRequested())
|
||||
try {
|
||||
const {data} = await fetchJSONFeedAJAX(url)
|
||||
// data could be from a webpage, and thus would be HTML
|
||||
if (typeof data === 'string' || !data) {
|
||||
dispatch(fetchJSONFeedFailed())
|
||||
} else {
|
||||
// decode HTML entities from response text
|
||||
const decodedData = {
|
||||
...data,
|
||||
items: data.items.map(item => {
|
||||
item.title = he.decode(item.title)
|
||||
item.content_text = he.decode(item.content_text)
|
||||
return item
|
||||
}),
|
||||
}
|
||||
|
||||
dispatch(fetchJSONFeedCompleted(decodedData))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(fetchJSONFeedFailed())
|
||||
dispatch(notify(notifyJSONFeedFailed(url)))
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import {notify} from 'src/shared/actions/notifications'
|
|||
import {notifyJSONFeedFailed} from 'src/shared/copy/notifications'
|
||||
|
||||
import {JSONFeedData} from 'src/types'
|
||||
import {AxiosResponse} from 'axios'
|
||||
|
||||
export enum ActionTypes {
|
||||
FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED',
|
||||
|
@ -52,7 +53,7 @@ export const fetchJSONFeedAsync = (url: string) => async (
|
|||
): Promise<void> => {
|
||||
dispatch(fetchJSONFeedRequested())
|
||||
try {
|
||||
const {data} = await fetchJSONFeedAJAX(url)
|
||||
const {data} = (await fetchJSONFeedAJAX(url)) as AxiosResponse<JSONFeedData>
|
||||
// data could be from a webpage, and thus would be HTML
|
||||
if (typeof data === 'string' || !data) {
|
||||
dispatch(fetchJSONFeedFailed())
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import AJAX from 'src/utils/ajax'
|
||||
import {JSONFeedData} from 'src/types'
|
||||
|
||||
const excludeBasepath = true // don't prefix route of external link with basepath/
|
||||
|
||||
export const fetchJSONFeed = url =>
|
||||
AJAX(
|
||||
export const fetchJSONFeed = (url: string) =>
|
||||
AJAX<JSONFeedData>(
|
||||
{
|
||||
method: 'GET',
|
||||
url,
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class GettingStarted extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FancyScrollbar className="getting-started--container">
|
||||
<div className="getting-started">
|
||||
<div className="getting-started--cell intro">
|
||||
<h5>
|
||||
<span className="icon cubo-uniform" /> Welcome to Chronograf!
|
||||
</h5>
|
||||
<p>Follow the links below to explore Chronograf’s features.</p>
|
||||
</div>
|
||||
<div className="getting-started--cell">
|
||||
<p>
|
||||
<strong>Install the TICK Stack</strong>
|
||||
<br />Save some time and use this handy tool to install the rest
|
||||
of the stack:
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/influxdata/sandbox" target="_blank">
|
||||
<span className="icon github" /> TICK Sandbox
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="getting-started--cell">
|
||||
<p>
|
||||
<strong>Guides</strong>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/create-a-dashboard/"
|
||||
target="_blank"
|
||||
>
|
||||
Create a Dashboard
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/create-a-kapacitor-alert/"
|
||||
target="_blank"
|
||||
>
|
||||
Create a Kapacitor Alert
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/configure-kapacitor-event-handlers/"
|
||||
target="_blank"
|
||||
>
|
||||
Configure Kapacitor Event Handlers
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/transition-web-admin-interface/"
|
||||
target="_blank"
|
||||
>
|
||||
Transition from InfluxDB's Web Admin Interface
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/dashboard-template-variables/"
|
||||
target="_blank"
|
||||
>
|
||||
Dashboard Template Variables
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
href="https://docs.influxdata.com/chronograf/latest/guides/advanced-kapacitor/"
|
||||
target="_blank"
|
||||
>
|
||||
Advanced Kapacitor Usage
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="getting-started--cell">
|
||||
<p>
|
||||
<strong>Questions & Comments</strong>
|
||||
</p>
|
||||
<p>
|
||||
If you have any product feedback please open a GitHub issue and
|
||||
we'll take a look. For any questions or other issues try posting
|
||||
on our
|
||||
<a href="https://community.influxdata.com/" target="_blank">
|
||||
Community Forum
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GettingStarted
|
|
@ -1,61 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
const JSONFeedReader = ({data}) =>
|
||||
data && data.items ? (
|
||||
<div className="newsfeed">
|
||||
{data.items
|
||||
? data.items.map(
|
||||
({
|
||||
id,
|
||||
date_published: datePublished,
|
||||
url,
|
||||
title,
|
||||
author: {name},
|
||||
image,
|
||||
content_text: contentText,
|
||||
}) => (
|
||||
<div key={id} className="newsfeed--post">
|
||||
<div className="newsfeed--date">
|
||||
{`${moment(datePublished).format('MMM DD')}`}
|
||||
</div>
|
||||
<div className="newsfeed--post-title">
|
||||
<a href={url} target="_blank">
|
||||
<h6>{title}</h6>
|
||||
</a>
|
||||
<span>by {name}</span>
|
||||
</div>
|
||||
<div className="newsfeed--content">
|
||||
{image ? <img src={image} /> : null}
|
||||
<p>{contentText}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
) : null
|
||||
|
||||
const {arrayOf, shape, string} = PropTypes
|
||||
|
||||
JSONFeedReader.propTypes = {
|
||||
data: shape({
|
||||
items: arrayOf(
|
||||
shape({
|
||||
author: shape({
|
||||
name: string.isRequired,
|
||||
}).isRequired,
|
||||
content_text: string.isRequired,
|
||||
date_published: string.isRequired,
|
||||
id: string.isRequired,
|
||||
image: string,
|
||||
title: string.isRequired,
|
||||
url: string.isRequired,
|
||||
})
|
||||
),
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default JSONFeedReader
|
|
@ -1,92 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {fetchJSONFeedAsync} from 'src/status/actions'
|
||||
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import JSONFeedReader from 'src/status/components/JSONFeedReader'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class NewsFeed extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
// TODO: implement shouldComponentUpdate based on fetching conditions
|
||||
|
||||
render() {
|
||||
const {hasCompletedFetchOnce, isFetching, isFailed, data} = this.props
|
||||
|
||||
if (!hasCompletedFetchOnce) {
|
||||
return isFailed ? (
|
||||
<div className="graph-empty">
|
||||
<p>Failed to load News Feed</p>
|
||||
</div>
|
||||
) : (
|
||||
// TODO: Factor this out of here and AutoRefresh
|
||||
<div className="graph-fetching">
|
||||
<div className="graph-spinner" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<FancyScrollbar autoHide={false} className="newsfeed--container">
|
||||
{isFetching ? (
|
||||
// TODO: Factor this out of here and AutoRefresh
|
||||
<div className="graph-panel__refreshing">
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
) : null}
|
||||
{isFailed ? (
|
||||
<div className="graph-empty">
|
||||
<p>Failed to refresh News Feed</p>
|
||||
</div>
|
||||
) : null}
|
||||
<JSONFeedReader data={data} />
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: implement interval polling a la AutoRefresh
|
||||
componentDidMount() {
|
||||
const {statusFeedURL, fetchJSONFeed} = this.props
|
||||
|
||||
fetchJSONFeed(statusFeedURL)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
NewsFeed.propTypes = {
|
||||
hasCompletedFetchOnce: bool.isRequired,
|
||||
isFetching: bool.isRequired,
|
||||
isFailed: bool.isRequired,
|
||||
data: shape(),
|
||||
fetchJSONFeed: func.isRequired,
|
||||
statusFeedURL: string,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
links: {
|
||||
external: {statusFeed: statusFeedURL},
|
||||
},
|
||||
JSONFeed: {hasCompletedFetchOnce, isFetching, isFailed, data},
|
||||
}) => ({
|
||||
hasCompletedFetchOnce,
|
||||
isFetching,
|
||||
isFailed,
|
||||
data,
|
||||
statusFeedURL,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchJSONFeed: bindActionCreators(fetchJSONFeedAsync, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(NewsFeed)
|
|
@ -1,6 +0,0 @@
|
|||
export const SET_STATUS_PAGE_AUTOREFRESH = 'SET_STATUS_PAGE_AUTOREFRESH'
|
||||
export const SET_STATUS_PAGE_TIME_RANGE = 'SET_STATUS_PAGE_TIME_RANGE'
|
||||
|
||||
export const FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED'
|
||||
export const FETCH_JSON_FEED_COMPLETED = 'FETCH_JSON_FEED_COMPLETED'
|
||||
export const FETCH_JSON_FEED_FAILED = 'FETCH_JSON_FEED_FAILED'
|
|
@ -1 +0,0 @@
|
|||
export const RECENT_ALERTS_LIMIT = 30
|
|
@ -1,113 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import LayoutRenderer from 'shared/components/LayoutRenderer'
|
||||
|
||||
import {fixtureStatusPageCells} from 'src/status/fixtures'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {
|
||||
TEMP_VAR_DASHBOARD_TIME,
|
||||
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||
} from 'src/shared/constants'
|
||||
|
||||
@ErrorHandling
|
||||
class StatusPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
cells: fixtureStatusPageCells,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {source, autoRefresh, timeRange} = this.props
|
||||
const {cells} = this.state
|
||||
|
||||
const dashboardTime = {
|
||||
id: 'dashtime',
|
||||
tempVar: TEMP_VAR_DASHBOARD_TIME,
|
||||
type: 'constant',
|
||||
values: [
|
||||
{
|
||||
value: timeRange.lower,
|
||||
type: 'constant',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const upperDashboardTime = {
|
||||
id: 'upperdashtime',
|
||||
tempVar: TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||
type: 'constant',
|
||||
values: [
|
||||
{
|
||||
value: 'now()',
|
||||
type: 'constant',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const templates = [dashboardTime, upperDashboardTime]
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Status</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<SourceIndicator />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="dashboard container-fluid full-width">
|
||||
{cells.length ? (
|
||||
<LayoutRenderer
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
cells={cells}
|
||||
templates={templates}
|
||||
source={source}
|
||||
shouldNotBeEditable={true}
|
||||
isStatusPage={true}
|
||||
isEditable={false}
|
||||
/>
|
||||
) : (
|
||||
<span>Loading Status Page...</span>
|
||||
)}
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {number, shape, string} = PropTypes
|
||||
|
||||
StatusPage.propTypes = {
|
||||
source: shape({
|
||||
name: string.isRequired,
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
autoRefresh: number.isRequired,
|
||||
timeRange: shape({
|
||||
lower: string.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({statusUI: {autoRefresh, timeRange}}) => ({
|
||||
autoRefresh,
|
||||
timeRange,
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, null)(StatusPage)
|
|
@ -1,9 +1,10 @@
|
|||
import React, {Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import SourceIndicator from 'src/shared/components/SourceIndicator'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import LayoutRenderer from 'src/shared/components/LayoutRenderer'
|
||||
import {STATUS_PAGE_TIME_RANGE} from 'src/shared/data/timeRanges'
|
||||
import {AUTOREFRESH_DEFAULT} from 'src/shared/constants'
|
||||
|
||||
import {fixtureStatusPageCells} from 'src/status/fixtures'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
@ -11,7 +12,7 @@ import {
|
|||
TEMP_VAR_DASHBOARD_TIME,
|
||||
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||
} from 'src/shared/constants'
|
||||
import {Source, TimeRange, Cell} from 'src/types'
|
||||
import {Source, Cell} from 'src/types'
|
||||
|
||||
interface State {
|
||||
cells: Cell[]
|
||||
|
@ -19,10 +20,11 @@ interface State {
|
|||
|
||||
interface Props {
|
||||
source: Source
|
||||
autoRefresh: number
|
||||
timeRange: TimeRange
|
||||
}
|
||||
|
||||
const autoRefresh = AUTOREFRESH_DEFAULT
|
||||
const timeRange = STATUS_PAGE_TIME_RANGE
|
||||
|
||||
@ErrorHandling
|
||||
class StatusPage extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
|
@ -34,7 +36,7 @@ class StatusPage extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {source, autoRefresh, timeRange} = this.props
|
||||
const {source} = this.props
|
||||
const {cells} = this.state
|
||||
|
||||
const dashboardTime = {
|
||||
|
@ -100,9 +102,4 @@ class StatusPage extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
const mstp = ({statusUI: {autoRefresh, timeRange}}) => ({
|
||||
autoRefresh,
|
||||
timeRange,
|
||||
})
|
||||
|
||||
export default connect(mstp, null)(StatusPage)
|
||||
export default StatusPage
|
||||
|
|
|
@ -1,9 +1,40 @@
|
|||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
||||
import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants/index'
|
||||
import {DEFAULT_AXIS} from 'src/dashboards/constants/cellEditor'
|
||||
import {Cell, CellQuery} from 'src/types'
|
||||
import {CellType} from 'src/types/dashboard'
|
||||
|
||||
export const fixtureStatusPageCells = [
|
||||
const emptyQuery: CellQuery = {
|
||||
query: '',
|
||||
source: '',
|
||||
queryConfig: {
|
||||
database: '',
|
||||
measurement: '',
|
||||
retentionPolicy: '',
|
||||
fields: [],
|
||||
tags: {},
|
||||
groupBy: {},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
},
|
||||
}
|
||||
|
||||
const emptyAxes = {
|
||||
axes: {
|
||||
x: DEFAULT_AXIS,
|
||||
y: DEFAULT_AXIS,
|
||||
y2: DEFAULT_AXIS,
|
||||
},
|
||||
}
|
||||
|
||||
export const fixtureStatusPageCells: Cell[] = [
|
||||
{
|
||||
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||
...emptyAxes,
|
||||
i: 'alerts-bar-graph',
|
||||
type: CellType.Bar,
|
||||
isWidget: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -15,7 +46,7 @@ export const fixtureStatusPageCells = [
|
|||
queries: [
|
||||
{
|
||||
query: `SELECT count("value") AS "count_value" FROM "chronograf"."autogen"."alerts" WHERE time > ${TEMP_VAR_DASHBOARD_TIME} GROUP BY time(1d)`,
|
||||
label: 'Events',
|
||||
source: '',
|
||||
queryConfig: {
|
||||
database: 'chronograf',
|
||||
measurement: 'alerts',
|
||||
|
@ -44,90 +75,56 @@ export const fixtureStatusPageCells = [
|
|||
},
|
||||
},
|
||||
],
|
||||
type: 'bar',
|
||||
links: {
|
||||
self: '/chronograf/v1/status/23/cells/c-bar-graphs-fly',
|
||||
},
|
||||
},
|
||||
{
|
||||
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||
...emptyAxes,
|
||||
i: 'recent-alerts',
|
||||
type: CellType.Alerts,
|
||||
isWidget: true,
|
||||
name: 'Alerts – Last 30 Days',
|
||||
type: 'alerts',
|
||||
x: 0,
|
||||
y: 5,
|
||||
w: 6.5,
|
||||
h: 6,
|
||||
legend: {},
|
||||
queries: [
|
||||
{
|
||||
query: '',
|
||||
queryConfig: {
|
||||
database: '',
|
||||
measurement: '',
|
||||
retentionPolicy: '',
|
||||
fields: [],
|
||||
tags: {},
|
||||
groupBy: {},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
queries: [emptyQuery],
|
||||
colors: DEFAULT_LINE_COLORS,
|
||||
links: {self: ''},
|
||||
},
|
||||
{
|
||||
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||
...emptyAxes,
|
||||
i: 'news-feed',
|
||||
type: CellType.News,
|
||||
isWidget: true,
|
||||
name: 'News Feed',
|
||||
type: 'news',
|
||||
x: 6.5,
|
||||
y: 5,
|
||||
w: 3,
|
||||
h: 6,
|
||||
legend: {},
|
||||
queries: [
|
||||
{
|
||||
query: '',
|
||||
queryConfig: {
|
||||
database: '',
|
||||
measurement: '',
|
||||
retentionPolicy: '',
|
||||
fields: [],
|
||||
tags: {},
|
||||
groupBy: {},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
queries: [emptyQuery],
|
||||
colors: DEFAULT_LINE_COLORS,
|
||||
links: {self: ''},
|
||||
},
|
||||
{
|
||||
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||
...emptyAxes,
|
||||
i: 'getting-started',
|
||||
type: CellType.Guide,
|
||||
isWidget: true,
|
||||
name: 'Getting Started',
|
||||
type: 'guide',
|
||||
x: 9.5,
|
||||
y: 5,
|
||||
w: 2.5,
|
||||
h: 6,
|
||||
legend: {},
|
||||
queries: [
|
||||
{
|
||||
query: '',
|
||||
queryConfig: {
|
||||
database: '',
|
||||
measurement: '',
|
||||
retentionPolicy: '',
|
||||
fields: [],
|
||||
tags: {},
|
||||
groupBy: {},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
queries: [emptyQuery],
|
||||
colors: DEFAULT_LINE_COLORS,
|
||||
links: {self: ''},
|
||||
},
|
||||
]
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import * as actionTypes from 'src/status/constants/actionTypes'
|
||||
|
||||
const initialState = {
|
||||
hasCompletedFetchOnce: false,
|
||||
isFetching: false,
|
||||
isFailed: false,
|
||||
data: null,
|
||||
}
|
||||
|
||||
const JSONFeedReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case actionTypes.FETCH_JSON_FEED_REQUESTED: {
|
||||
return {...state, isFetching: true, isFailed: false}
|
||||
}
|
||||
|
||||
case actionTypes.FETCH_JSON_FEED_COMPLETED: {
|
||||
const {data} = action.payload
|
||||
|
||||
return {
|
||||
...state,
|
||||
hasCompletedFetchOnce: true,
|
||||
isFetching: false,
|
||||
isFailed: false,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
case actionTypes.FETCH_JSON_FEED_FAILED: {
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
isFailed: true,
|
||||
data: null,
|
||||
}
|
||||
}
|
||||
|
||||
default: {
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default JSONFeedReducer
|
|
@ -1,7 +0,0 @@
|
|||
import statusUI from './ui'
|
||||
import JSONFeed from './JSONFeed'
|
||||
|
||||
export default {
|
||||
statusUI,
|
||||
JSONFeed,
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import {AUTOREFRESH_DEFAULT} from 'shared/constants'
|
||||
import {timeRanges} from 'shared/data/timeRanges'
|
||||
|
||||
import * as actionTypes from 'src/status/constants/actionTypes'
|
||||
|
||||
const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 30d')
|
||||
|
||||
const initialState = {
|
||||
autoRefresh: AUTOREFRESH_DEFAULT,
|
||||
timeRange: {lower, upper},
|
||||
}
|
||||
|
||||
const statusUI = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case actionTypes.SET_STATUS_PAGE_AUTOREFRESH: {
|
||||
const {milliseconds} = action.payload
|
||||
|
||||
return {
|
||||
...state,
|
||||
autoRefresh: milliseconds,
|
||||
}
|
||||
}
|
||||
|
||||
case actionTypes.SET_STATUS_PAGE_TIME_RANGE: {
|
||||
const {timeRange} = action.payload
|
||||
|
||||
return {...state, timeRange}
|
||||
}
|
||||
|
||||
default: {
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default statusUI
|
|
@ -3,12 +3,12 @@ import {ColorString} from 'src/types/colors'
|
|||
import {Template} from 'src/types/tempVars'
|
||||
|
||||
export interface Axis {
|
||||
bounds: [string, string]
|
||||
label: string
|
||||
prefix: string
|
||||
suffix: string
|
||||
base: string
|
||||
scale: string
|
||||
bounds?: [string, string]
|
||||
}
|
||||
|
||||
export type TimeSeriesValue = string | number | null | undefined
|
||||
|
@ -76,6 +76,7 @@ export interface Cell {
|
|||
decimalPlaces: DecimalPlaces
|
||||
links: CellLinks
|
||||
legend: Legend
|
||||
isWidget?: boolean
|
||||
}
|
||||
|
||||
export enum CellType {
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
DygraphClass,
|
||||
DygraphData,
|
||||
} from './dygraphs'
|
||||
import {JSONFeedData} from './status'
|
||||
|
||||
export {
|
||||
Me,
|
||||
|
@ -96,4 +97,5 @@ export {
|
|||
SchemaFilter,
|
||||
RemoteDataState,
|
||||
URLQueryParams,
|
||||
JSONFeedData,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue