Display kapacitor name and url in header

pull/4675/head
Andrew Watkins 2018-10-30 13:41:48 -07:00
parent 363883c598
commit 5983ef2dfc
7 changed files with 113 additions and 65 deletions

View File

@ -1,17 +1,15 @@
import React, {SFC} from 'react' import React, {SFC} from 'react'
import {Link} from 'react-router' import {Link} from 'react-router'
import NoKapacitorError from 'src/shared/components/NoKapacitorError'
import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable'
import TasksTable from 'src/kapacitor/components/TasksTable' import TasksTable from 'src/kapacitor/components/TasksTable'
import {Source, AlertRule} from 'src/types' import {Source, AlertRule, Kapacitor} from 'src/types'
interface KapacitorRulesProps { interface KapacitorRulesProps {
source: Source source: Source
rules: AlertRule[] rules: AlertRule[]
hasKapacitor: boolean kapacitor: Kapacitor
loading: boolean
onDelete: (rule: AlertRule) => void onDelete: (rule: AlertRule) => void
onChangeRuleStatus: (rule: AlertRule) => void onChangeRuleStatus: (rule: AlertRule) => void
} }
@ -19,35 +17,10 @@ interface KapacitorRulesProps {
const KapacitorRules: SFC<KapacitorRulesProps> = ({ const KapacitorRules: SFC<KapacitorRulesProps> = ({
source, source,
rules, rules,
hasKapacitor,
loading,
onDelete, onDelete,
onChangeRuleStatus, onChangeRuleStatus,
}) => { }) => {
if (loading || !hasKapacitor) {
return (
<div>
<div className="panel-heading">
<h2 className="panel-title">Alert Rules</h2>
<button className="btn btn-primary btn-sm disabled" disabled={true}>
Create Rule
</button>
</div>
<div className="panel-body">
<div className="generic-empty-state">
{!hasKapacitor ? (
<NoKapacitorError source={source} />
) : (
<p>Loading Rules...</p>
)}
</div>
</div>
</div>
)
}
const builderRules = rules.filter((r: AlertRule) => r.query) const builderRules = rules.filter((r: AlertRule) => r.query)
const builderHeader = `${builderRules.length} Alert Rule${ const builderHeader = `${builderRules.length} Alert Rule${
builderRules.length === 1 ? '' : 's' builderRules.length === 1 ? '' : 's'
}` }`

View File

@ -103,6 +103,7 @@ class KapacitorRulePage extends Component<Props, State> {
if (!query) { if (!query) {
return <div className="page-spinner" /> return <div className="page-spinner" />
} }
return ( return (
<KapacitorRule <KapacitorRule
source={source} source={source}

View File

@ -1,17 +1,25 @@
// Libraries
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
// APIs
import {getActiveKapacitor} from 'src/shared/apis' import {getActiveKapacitor} from 'src/shared/apis'
// Actions
import * as kapacitorActionCreators from '../actions/view' import * as kapacitorActionCreators from '../actions/view'
// Components
import KapacitorRules from 'src/kapacitor/components/KapacitorRules' import KapacitorRules from 'src/kapacitor/components/KapacitorRules'
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip' import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
import {Page} from 'src/reusable_ui' import {Page, Spinner} from 'src/reusable_ui'
import {Source, Kapacitor, AlertRule} from 'src/types' // Types
import {Source, Kapacitor, AlertRule, RemoteDataState} from 'src/types'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import NoKapacitorError from 'src/shared/components/NoKapacitorError'
interface Props { interface Props {
source: Source source: Source
@ -25,8 +33,8 @@ interface Props {
} }
interface State { interface State {
hasKapacitor: boolean kapacitor: Kapacitor
loading: boolean loading: RemoteDataState
} }
@ErrorHandling @ErrorHandling
@ -34,30 +42,30 @@ export class KapacitorRulesPage extends PureComponent<Props, State> {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
hasKapacitor: false, kapacitor: null,
loading: true, loading: RemoteDataState.NotStarted,
} }
} }
public async componentDidMount() { public async componentDidMount() {
const {source, actions} = this.props const {source, actions} = this.props
this.setState({loading: RemoteDataState.Loading})
const kapacitor: Kapacitor = await getActiveKapacitor(source) const kapacitor: Kapacitor = await getActiveKapacitor(source)
if (!kapacitor) { if (!kapacitor) {
return return this.setState({loading: RemoteDataState.Done, kapacitor: null})
} }
await actions.fetchRules(kapacitor) await actions.fetchRules(kapacitor)
this.setState({loading: false, hasKapacitor: !!kapacitor}) this.setState({loading: RemoteDataState.Done, kapacitor})
} }
public render() { public render() {
const {source, rules} = this.props
const {hasKapacitor, loading} = this.state
return ( return (
<Page> <Page className={this.className}>
<Page.Header> <Page.Header>
<Page.Header.Left> <Page.Header.Left>
<Page.Title title="Manage Tasks" /> <Page.Title title={this.headerTitle} />
</Page.Header.Left> </Page.Header.Left>
<Page.Header.Right showSourceIndicator={true}> <Page.Header.Right showSourceIndicator={true}>
<QuestionMarkTooltip <QuestionMarkTooltip
@ -67,19 +75,51 @@ export class KapacitorRulesPage extends PureComponent<Props, State> {
</Page.Header.Right> </Page.Header.Right>
</Page.Header> </Page.Header>
<Page.Contents> <Page.Contents>
<KapacitorRules <Spinner loading={this.state.loading}>{this.rules}</Spinner>
source={source}
rules={rules}
hasKapacitor={hasKapacitor}
loading={loading}
onDelete={this.handleDeleteRule}
onChangeRuleStatus={this.handleRuleStatus}
/>
</Page.Contents> </Page.Contents>
</Page> </Page>
) )
} }
private get rules(): JSX.Element {
const {kapacitor} = this.state
const {source, rules} = this.props
if (!kapacitor) {
return <NoKapacitorError source={source} />
}
return (
<KapacitorRules
rules={rules}
source={source}
kapacitor={kapacitor}
onDelete={this.handleDeleteRule}
onChangeRuleStatus={this.handleRuleStatus}
/>
)
}
private get headerTitle(): string {
const {kapacitor} = this.state
if (!kapacitor) {
return 'Manage Tasks'
}
return `Manage Tasks on "${kapacitor.name}" @ ${kapacitor.url}`
}
private get className(): string {
const {kapacitor} = this.state
if (!kapacitor) {
return 'empty-tasks-page'
}
return ''
}
private handleDeleteRule = (rule: AlertRule) => { private handleDeleteRule = (rule: AlertRule) => {
const {actions} = this.props const {actions} = this.props

View File

@ -0,0 +1,33 @@
// Libraries
import React, {Component} from 'react'
// Types
import {RemoteDataState} from 'src/types'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
loading: RemoteDataState
children: JSX.Element[] | JSX.Element
}
@ErrorHandling
export default class Spinner extends Component<Props> {
public render() {
return this.children
}
private get children(): JSX.Element | JSX.Element[] {
const {loading, children} = this.props
if (
loading === RemoteDataState.Loading ||
loading === RemoteDataState.NotStarted
) {
return <div className="spinner" />
}
return children
}
}

View File

@ -12,6 +12,7 @@ import Page from './components/page_layout/Page'
import Panel, {PanelType} from './components/panel/Panel' import Panel, {PanelType} from './components/panel/Panel'
import Radio from './components/radio_buttons/RadioButtons' import Radio from './components/radio_buttons/RadioButtons'
import SlideToggle from './components/slide_toggle/SlideToggle' import SlideToggle from './components/slide_toggle/SlideToggle'
import Spinner from './components/spinners/Spinner'
// Import Types // Import Types
import { import {
@ -53,4 +54,5 @@ export {
Greys, Greys,
IconFont, IconFont,
Columns, Columns,
Spinner,
} }

View File

@ -51,9 +51,7 @@
---------------------------------------------- ----------------------------------------------
*/ */
.sortable-header { .sortable-header {
transition: transition: color 0.25s ease, background-color 0.25s ease;
color 0.25s ease,
background-color 0.25s ease;
position: relative; position: relative;
> .icon { > .icon {
@ -64,10 +62,7 @@
color: $g20-white; color: $g20-white;
opacity: 0; opacity: 0;
transform: translateY(-50%); transform: translateY(-50%);
transition: transition: opacity 0.25s ease, color 0.25s ease, transform 0.35s ease;
opacity 0.25s ease,
color 0.25s ease,
transform 0.35s ease;
} }
&:hover { &:hover {
@ -80,7 +75,9 @@
background-color: $g5-pepper; background-color: $g5-pepper;
color: $g19-ghost; color: $g19-ghost;
> .icon {opacity: 1;} > .icon {
opacity: 1;
}
} }
&.sorting-ascending > .icon { &.sorting-ascending > .icon {
transform: translateY(-50%) rotate(180deg); transform: translateY(-50%) rotate(180deg);
@ -140,9 +137,7 @@ $table-tab-scrollbar-height: 6px;
background-color: $g4-onyx; background-color: $g4-onyx;
color: $g11-sidewalk; color: $g11-sidewalk;
margin-right: 2px; margin-right: 2px;
transition: transition: color 0.25s ease, background-color 0.25s ease;
color 0.25s ease,
background-color 0.25s ease;
&:hover { &:hover {
background-color: $g5-pepper; background-color: $g5-pepper;
@ -169,7 +164,6 @@ $table-tab-scrollbar-height: 6px;
border-radius: 0 $radius-small $radius-small $radius-small; border-radius: 0 $radius-small $radius-small $radius-small;
} }
.table > tbody > tr.highlight, .table > tbody > tr.highlight,
.table.table-highlight > tbody > tr.highlight { .table.table-highlight > tbody > tr.highlight {
background-color: $g4-onyx; background-color: $g4-onyx;
@ -180,6 +174,7 @@ $table-tab-scrollbar-height: 6px;
---------------------------------------------- ----------------------------------------------
*/ */
.alert-history-page, .alert-history-page,
.empty-tasks-page,
.hosts-list-page { .hosts-list-page {
.container-fluid, .container-fluid,
.row, .row,

View File

@ -8,13 +8,13 @@ import NoKapacitorError from 'src/shared/components/NoKapacitorError'
import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable' import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable'
import TasksTable from 'src/kapacitor/components/TasksTable' import TasksTable from 'src/kapacitor/components/TasksTable'
import {source, kapacitorRules} from 'test/resources' import {source, kapacitorRules, kapacitor} from 'test/resources'
describe('Kapacitor.Containers.KapacitorRules', () => { describe('Kapacitor.Containers.KapacitorRules', () => {
const props = { const props = {
source, source,
rules: kapacitorRules, rules: kapacitorRules,
hasKapacitor: true, kapacitor,
loading: false, loading: false,
onDelete: () => {}, onDelete: () => {},
onChangeRuleStatus: () => {}, onChangeRuleStatus: () => {},
@ -52,7 +52,11 @@ describe('Kapacitor.Containers.KapacitorRules', () => {
}) })
it('renders NoKapacitorError if not loading and no kapacitor configured', () => { it('renders NoKapacitorError if not loading and no kapacitor configured', () => {
const noKapacitorProps = {...props, loading: false, hasKapacitor: false} const noKapacitorProps = {
...props,
loading: false,
hasKapacitor: false,
}
const wrapper = shallow(<KapacitorRules {...noKapacitorProps} />) const wrapper = shallow(<KapacitorRules {...noKapacitorProps} />)
const noKapacitorError = wrapper.find(NoKapacitorError) const noKapacitorError = wrapper.find(NoKapacitorError)
expect(noKapacitorError.length).toEqual(1) expect(noKapacitorError.length).toEqual(1)