Typescriptify Status Page and children
parent
9b51924921
commit
5d7958988f
|
@ -48,7 +48,7 @@ interface State {
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
export class AllUsersPage extends PureComponent<Props, State> {
|
export class AllUsersPage extends PureComponent<Props, State> {
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {proxy} from 'src/utils/queryUrlGenerator'
|
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||||
import {TimeRange} from '../../types'
|
import {TimeRange} from 'src/types'
|
||||||
|
|
||||||
export const getAlerts = (
|
export const getAlerts = (
|
||||||
source: string,
|
source: string,
|
||||||
|
|
|
@ -3,15 +3,38 @@ import {noop} from 'src/shared/actions/app'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import {errorThrown} from 'src/shared/actions/errors'
|
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, {
|
editQueryStatus(query.id, {
|
||||||
loading: true,
|
loading: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {results: [{}]}
|
// {results: [{}]}
|
||||||
export const handleSuccess = (data, query, editQueryStatus) => {
|
export const handleSuccess = (
|
||||||
|
data: TimeSeriesResponse,
|
||||||
|
query: Query,
|
||||||
|
editQueryStatus
|
||||||
|
) => {
|
||||||
const {results} = data
|
const {results} = data
|
||||||
const error = _.get(results, ['0', 'error'], false)
|
const error = _.get(results, ['0', 'error'], false)
|
||||||
const series = _.get(results, ['0', 'series'], 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 (
|
export const fetchTimeSeriesAsync = async (
|
||||||
{source, db, rp, query, tempVars, resolution}: Payload,
|
{source, db, rp, query, tempVars, resolution}: Payload,
|
||||||
editQueryStatus = noop
|
editQueryStatus = noop
|
||||||
|
|
|
@ -175,8 +175,10 @@ class Dygraph extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
this.dygraph.destroy()
|
if (this.dygraph) {
|
||||||
delete this.dygraph
|
this.dygraph.destroy()
|
||||||
|
delete this.dygraph
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public shouldComponentUpdate(nextProps: Props, nextState: State) {
|
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_V_MARGIN = 8
|
||||||
export const DYGRAPH_CONTAINER_XLABEL_MARGIN = 20
|
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 = {
|
export const DEFAULT_SOURCE = {
|
||||||
|
id: '',
|
||||||
url: 'http://localhost:8086',
|
url: 'http://localhost:8086',
|
||||||
name: 'Influx 1',
|
name: 'Influx 1',
|
||||||
username: '',
|
username: '',
|
||||||
|
@ -436,6 +452,11 @@ export const DEFAULT_SOURCE = {
|
||||||
telegraf: 'telegraf',
|
telegraf: 'telegraf',
|
||||||
insecureSkipVerify: false,
|
insecureSkipVerify: false,
|
||||||
metaUrl: '',
|
metaUrl: '',
|
||||||
|
organization: '',
|
||||||
|
role: '',
|
||||||
|
defaultRP: '',
|
||||||
|
links: DEFAULT_SOURCE_LINKS,
|
||||||
|
type: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultIntervalValue = '333'
|
export const defaultIntervalValue = '333'
|
||||||
|
|
|
@ -6,6 +6,8 @@ interface TimeRangeOption extends TimeRange {
|
||||||
menuOption: string
|
menuOption: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nowminus30d = 'now() - 30d'
|
||||||
|
|
||||||
export const timeRanges: TimeRangeOption[] = [
|
export const timeRanges: TimeRangeOption[] = [
|
||||||
{
|
{
|
||||||
defaultGroupBy: '10s',
|
defaultGroupBy: '10s',
|
||||||
|
@ -75,7 +77,7 @@ export const timeRanges: TimeRangeOption[] = [
|
||||||
defaultGroupBy: '6h',
|
defaultGroupBy: '6h',
|
||||||
seconds: 2592000,
|
seconds: 2592000,
|
||||||
inputValue: 'Past 30d',
|
inputValue: 'Past 30d',
|
||||||
lower: 'now() - 30d',
|
lower: nowminus30d,
|
||||||
upper: null,
|
upper: null,
|
||||||
menuOption: 'Past 30d',
|
menuOption: 'Past 30d',
|
||||||
},
|
},
|
||||||
|
@ -89,3 +91,7 @@ export const defaultTimeRange = {
|
||||||
seconds: 900,
|
seconds: 900,
|
||||||
format: FORMAT_INFLUXQL,
|
format: FORMAT_INFLUXQL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const STATUS_PAGE_TIME_RANGE = timeRanges.find(
|
||||||
|
tr => tr.lower === nowminus30d
|
||||||
|
)
|
||||||
|
|
|
@ -14,12 +14,12 @@ import {
|
||||||
notifySourceDeleteFailed,
|
notifySourceDeleteFailed,
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {Source, NotificationFunc} from 'src/types'
|
import {Source, Notification} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source: Source
|
source: Source
|
||||||
sources: Source[]
|
sources: Source[]
|
||||||
notify: (n: NotificationFunc) => void
|
notify: (n: Notification) => void
|
||||||
deleteKapacitor: actions.DeleteKapacitorAsync
|
deleteKapacitor: actions.DeleteKapacitorAsync
|
||||||
fetchKapacitors: actions.FetchKapacitorsAsync
|
fetchKapacitors: actions.FetchKapacitorsAsync
|
||||||
removeAndLoadSources: actions.RemoveAndLoadSources
|
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 React, {Component, ChangeEvent, FormEvent} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import {withRouter, InjectedRouter} from 'react-router'
|
||||||
import {withRouter} from 'react-router'
|
import {Location} from 'history'
|
||||||
import _ from 'lodash'
|
|
||||||
import {getSource} from 'src/shared/apis'
|
import {getSource} from 'src/shared/apis'
|
||||||
import {createSource, updateSource} from 'src/shared/apis'
|
import {createSource, updateSource} from 'src/shared/apis'
|
||||||
import {
|
import {
|
||||||
|
@ -27,45 +26,47 @@ import {
|
||||||
notifySourceUdpateFailed,
|
notifySourceUdpateFailed,
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
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 {
|
interface Props {
|
||||||
params: shape({
|
location: Location
|
||||||
id: string,
|
router: InjectedRouter
|
||||||
sourceID: string,
|
params: Params
|
||||||
}),
|
notify: (notification: Notification | NotificationFunc) => void
|
||||||
router: shape({
|
addSource: (s: Source) => void
|
||||||
push: func.isRequired,
|
updateSource: (s: Source) => void
|
||||||
}).isRequired,
|
}
|
||||||
location: shape({
|
|
||||||
query: shape({
|
|
||||||
redirectPath: string,
|
|
||||||
}).isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
notify: func.isRequired,
|
|
||||||
addSource: func.isRequired,
|
|
||||||
updateSource: func.isRequired,}
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
|
isCreated: boolean
|
||||||
source: Source
|
source: Source
|
||||||
editMode: boolean
|
editMode: boolean
|
||||||
isInitialSource: boolean}
|
isInitialSource: boolean
|
||||||
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class SourcePage extends Component<Props, State> {
|
class SourcePage extends Component<Props, State> {
|
||||||
constructor(props:Props) {
|
constructor(props: Props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
|
isCreated: false,
|
||||||
source: DEFAULT_SOURCE,
|
source: DEFAULT_SOURCE,
|
||||||
editMode: props.params.id !== undefined,
|
editMode: props.params.id !== undefined,
|
||||||
isInitialSource: props.router.location.pathname === initialPath,
|
isInitialSource: props.location.pathname === initialPath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount() {
|
||||||
const {editMode} = this.state
|
const {editMode} = this.state
|
||||||
const {params, notify} = this.props
|
const {params, notify} = this.props
|
||||||
|
|
||||||
|
@ -81,148 +82,12 @@ class SourcePage extends Component<Props, State> {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
notify(notifyErrorConnectingToSource(this._parseError(error)))
|
notify(notifyErrorConnectingToSource(this.parseError(error)))
|
||||||
this.setState({isLoading: false})
|
this.setState({isLoading: false})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange = e => {
|
public render() {
|
||||||
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
|
const {isLoading, source, editMode, isInitialSource} = this.state
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
@ -272,26 +137,143 @@ class SourcePage extends Component<Props, State> {
|
||||||
</div>
|
</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 = {
|
if (e.target.type === 'checkbox') {
|
||||||
params: shape({
|
val = e.target.checked
|
||||||
id: string,
|
}
|
||||||
sourceID: string,
|
|
||||||
}),
|
this.setState(prevState => {
|
||||||
router: shape({
|
const source = {
|
||||||
push: func.isRequired,
|
...prevState.source,
|
||||||
}).isRequired,
|
[name]: val,
|
||||||
location: shape({
|
}
|
||||||
query: shape({
|
|
||||||
redirectPath: string,
|
return {...prevState, source}
|
||||||
}).isRequired,
|
})
|
||||||
}).isRequired,
|
}
|
||||||
notify: func.isRequired,
|
|
||||||
addSource: func.isRequired,
|
private handleBlurSourceURL = () => {
|
||||||
updateSource: func.isRequired,
|
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 => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
@ -299,4 +281,4 @@ const mapDispatchToProps = dispatch => ({
|
||||||
addSource: bindActionCreators(addSourceAction, dispatch),
|
addSource: bindActionCreators(addSourceAction, dispatch),
|
||||||
updateSource: bindActionCreators(updateSourceAction, 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 {notifyJSONFeedFailed} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {JSONFeedData} from 'src/types'
|
import {JSONFeedData} from 'src/types'
|
||||||
|
import {AxiosResponse} from 'axios'
|
||||||
|
|
||||||
export enum ActionTypes {
|
export enum ActionTypes {
|
||||||
FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED',
|
FETCH_JSON_FEED_REQUESTED = 'FETCH_JSON_FEED_REQUESTED',
|
||||||
|
@ -52,7 +53,7 @@ export const fetchJSONFeedAsync = (url: string) => async (
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
dispatch(fetchJSONFeedRequested())
|
dispatch(fetchJSONFeedRequested())
|
||||||
try {
|
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
|
// data could be from a webpage, and thus would be HTML
|
||||||
if (typeof data === 'string' || !data) {
|
if (typeof data === 'string' || !data) {
|
||||||
dispatch(fetchJSONFeedFailed())
|
dispatch(fetchJSONFeedFailed())
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import AJAX from 'src/utils/ajax'
|
import AJAX from 'src/utils/ajax'
|
||||||
|
import {JSONFeedData} from 'src/types'
|
||||||
|
|
||||||
const excludeBasepath = true // don't prefix route of external link with basepath/
|
const excludeBasepath = true // don't prefix route of external link with basepath/
|
||||||
|
|
||||||
export const fetchJSONFeed = url =>
|
export const fetchJSONFeed = (url: string) =>
|
||||||
AJAX(
|
AJAX<JSONFeedData>(
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url,
|
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 React, {Component} from 'react'
|
||||||
import {connect} from 'react-redux'
|
|
||||||
|
|
||||||
import SourceIndicator from 'src/shared/components/SourceIndicator'
|
import SourceIndicator from 'src/shared/components/SourceIndicator'
|
||||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
import LayoutRenderer from 'src/shared/components/LayoutRenderer'
|
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 {fixtureStatusPageCells} from 'src/status/fixtures'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
@ -11,7 +12,7 @@ import {
|
||||||
TEMP_VAR_DASHBOARD_TIME,
|
TEMP_VAR_DASHBOARD_TIME,
|
||||||
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
TEMP_VAR_UPPER_DASHBOARD_TIME,
|
||||||
} from 'src/shared/constants'
|
} from 'src/shared/constants'
|
||||||
import {Source, TimeRange, Cell} from 'src/types'
|
import {Source, Cell} from 'src/types'
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
cells: Cell[]
|
cells: Cell[]
|
||||||
|
@ -19,10 +20,11 @@ interface State {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source: Source
|
source: Source
|
||||||
autoRefresh: number
|
|
||||||
timeRange: TimeRange
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const autoRefresh = AUTOREFRESH_DEFAULT
|
||||||
|
const timeRange = STATUS_PAGE_TIME_RANGE
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
class StatusPage extends Component<Props, State> {
|
class StatusPage extends Component<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
|
@ -34,7 +36,7 @@ class StatusPage extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {source, autoRefresh, timeRange} = this.props
|
const {source} = this.props
|
||||||
const {cells} = this.state
|
const {cells} = this.state
|
||||||
|
|
||||||
const dashboardTime = {
|
const dashboardTime = {
|
||||||
|
@ -100,9 +102,4 @@ class StatusPage extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mstp = ({statusUI: {autoRefresh, timeRange}}) => ({
|
export default StatusPage
|
||||||
autoRefresh,
|
|
||||||
timeRange,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default connect(mstp, null)(StatusPage)
|
|
||||||
|
|
|
@ -1,9 +1,40 @@
|
||||||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||||
import {TEMP_VAR_DASHBOARD_TIME} from 'src/shared/constants'
|
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',
|
i: 'alerts-bar-graph',
|
||||||
|
type: CellType.Bar,
|
||||||
isWidget: false,
|
isWidget: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -15,7 +46,7 @@ export const fixtureStatusPageCells = [
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
query: `SELECT count("value") AS "count_value" FROM "chronograf"."autogen"."alerts" WHERE time > ${TEMP_VAR_DASHBOARD_TIME} GROUP BY time(1d)`,
|
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: {
|
queryConfig: {
|
||||||
database: 'chronograf',
|
database: 'chronograf',
|
||||||
measurement: 'alerts',
|
measurement: 'alerts',
|
||||||
|
@ -44,90 +75,56 @@ export const fixtureStatusPageCells = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: 'bar',
|
|
||||||
links: {
|
links: {
|
||||||
self: '/chronograf/v1/status/23/cells/c-bar-graphs-fly',
|
self: '/chronograf/v1/status/23/cells/c-bar-graphs-fly',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||||
|
...emptyAxes,
|
||||||
i: 'recent-alerts',
|
i: 'recent-alerts',
|
||||||
|
type: CellType.Alerts,
|
||||||
isWidget: true,
|
isWidget: true,
|
||||||
name: 'Alerts – Last 30 Days',
|
name: 'Alerts – Last 30 Days',
|
||||||
type: 'alerts',
|
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 5,
|
y: 5,
|
||||||
w: 6.5,
|
w: 6.5,
|
||||||
h: 6,
|
h: 6,
|
||||||
legend: {},
|
legend: {},
|
||||||
queries: [
|
queries: [emptyQuery],
|
||||||
{
|
colors: DEFAULT_LINE_COLORS,
|
||||||
query: '',
|
links: {self: ''},
|
||||||
queryConfig: {
|
|
||||||
database: '',
|
|
||||||
measurement: '',
|
|
||||||
retentionPolicy: '',
|
|
||||||
fields: [],
|
|
||||||
tags: {},
|
|
||||||
groupBy: {},
|
|
||||||
areTagsAccepted: false,
|
|
||||||
rawText: null,
|
|
||||||
range: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||||
|
...emptyAxes,
|
||||||
i: 'news-feed',
|
i: 'news-feed',
|
||||||
|
type: CellType.News,
|
||||||
isWidget: true,
|
isWidget: true,
|
||||||
name: 'News Feed',
|
name: 'News Feed',
|
||||||
type: 'news',
|
|
||||||
x: 6.5,
|
x: 6.5,
|
||||||
y: 5,
|
y: 5,
|
||||||
w: 3,
|
w: 3,
|
||||||
h: 6,
|
h: 6,
|
||||||
legend: {},
|
legend: {},
|
||||||
queries: [
|
queries: [emptyQuery],
|
||||||
{
|
colors: DEFAULT_LINE_COLORS,
|
||||||
query: '',
|
links: {self: ''},
|
||||||
queryConfig: {
|
|
||||||
database: '',
|
|
||||||
measurement: '',
|
|
||||||
retentionPolicy: '',
|
|
||||||
fields: [],
|
|
||||||
tags: {},
|
|
||||||
groupBy: {},
|
|
||||||
areTagsAccepted: false,
|
|
||||||
rawText: null,
|
|
||||||
range: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
...NEW_DEFAULT_DASHBOARD_CELL,
|
||||||
|
...emptyAxes,
|
||||||
i: 'getting-started',
|
i: 'getting-started',
|
||||||
|
type: CellType.Guide,
|
||||||
isWidget: true,
|
isWidget: true,
|
||||||
name: 'Getting Started',
|
name: 'Getting Started',
|
||||||
type: 'guide',
|
|
||||||
x: 9.5,
|
x: 9.5,
|
||||||
y: 5,
|
y: 5,
|
||||||
w: 2.5,
|
w: 2.5,
|
||||||
h: 6,
|
h: 6,
|
||||||
legend: {},
|
legend: {},
|
||||||
queries: [
|
queries: [emptyQuery],
|
||||||
{
|
colors: DEFAULT_LINE_COLORS,
|
||||||
query: '',
|
links: {self: ''},
|
||||||
queryConfig: {
|
|
||||||
database: '',
|
|
||||||
measurement: '',
|
|
||||||
retentionPolicy: '',
|
|
||||||
fields: [],
|
|
||||||
tags: {},
|
|
||||||
groupBy: {},
|
|
||||||
areTagsAccepted: false,
|
|
||||||
rawText: null,
|
|
||||||
range: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -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'
|
import {Template} from 'src/types/tempVars'
|
||||||
|
|
||||||
export interface Axis {
|
export interface Axis {
|
||||||
bounds: [string, string]
|
|
||||||
label: string
|
label: string
|
||||||
prefix: string
|
prefix: string
|
||||||
suffix: string
|
suffix: string
|
||||||
base: string
|
base: string
|
||||||
scale: string
|
scale: string
|
||||||
|
bounds?: [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimeSeriesValue = string | number | null | undefined
|
export type TimeSeriesValue = string | number | null | undefined
|
||||||
|
@ -76,6 +76,7 @@ export interface Cell {
|
||||||
decimalPlaces: DecimalPlaces
|
decimalPlaces: DecimalPlaces
|
||||||
links: CellLinks
|
links: CellLinks
|
||||||
legend: Legend
|
legend: Legend
|
||||||
|
isWidget?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CellType {
|
export enum CellType {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import {
|
||||||
DygraphClass,
|
DygraphClass,
|
||||||
DygraphData,
|
DygraphData,
|
||||||
} from './dygraphs'
|
} from './dygraphs'
|
||||||
|
import {JSONFeedData} from './status'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Me,
|
Me,
|
||||||
|
@ -96,4 +97,5 @@ export {
|
||||||
SchemaFilter,
|
SchemaFilter,
|
||||||
RemoteDataState,
|
RemoteDataState,
|
||||||
URLQueryParams,
|
URLQueryParams,
|
||||||
|
JSONFeedData,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue