Refactor InfluxTable and convert to ts

pull/10616/head
Andrew Watkins 2018-05-17 12:22:24 -07:00
parent c78f97972d
commit 149cd4a27f
11 changed files with 425 additions and 251 deletions

View File

@ -0,0 +1,56 @@
import React, {PureComponent} from 'react'
import {Link} from 'react-router'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import {Source} from 'src/types'
interface Props {
source: Source
currentSource: Source
}
class ConnectionLink extends PureComponent<Props> {
public render() {
const {source} = this.props
return (
<h5 className="margin-zero">
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={<strong>{source.name}</strong>}
>
<Link
to={`${location.pathname}/${source.id}/edit`}
className={this.className}
>
<strong>{source.name}</strong>
{this.default}
</Link>
</Authorized>
</h5>
)
}
private get className(): string {
if (this.isCurrentSource) {
return 'link-success'
}
return ''
}
private get default(): string {
const {source} = this.props
if (source.default) {
return ' (Default)'
}
return ''
}
private get isCurrentSource(): boolean {
const {source, currentSource} = this.props
return source.id === currentSource.id
}
}
export default ConnectionLink

View File

@ -1,247 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import {Link, withRouter} from 'react-router'
import {connect} from 'react-redux'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import Dropdown from 'shared/components/Dropdown'
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
import ConfirmButton from 'shared/components/ConfirmButton'
const kapacitorDropdown = (
kapacitors,
source,
router,
setActiveKapacitor,
handleDeleteKapacitor
) => {
if (!kapacitors || kapacitors.length === 0) {
return (
<Authorized requiredRole={EDITOR_ROLE}>
<Link
to={`/sources/${source.id}/kapacitors/new`}
className="btn btn-xs btn-default"
>
<span className="icon plus" /> Add Kapacitor Connection
</Link>
</Authorized>
)
}
const kapacitorItems = kapacitors.map(k => {
return {
text: k.name,
resource: `/sources/${source.id}/kapacitors/${k.id}`,
kapacitor: k,
}
})
const activeKapacitor = kapacitors.find(k => k.active)
let selected = ''
if (activeKapacitor) {
selected = activeKapacitor.name
} else {
selected = kapacitorItems[0].text
}
const unauthorizedDropdown = (
<div className="source-table--kapacitor__view-only">{selected}</div>
)
return (
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={unauthorizedDropdown}
>
<Dropdown
className="dropdown-260"
buttonColor="btn-primary"
buttonSize="btn-xs"
items={kapacitorItems}
onChoose={setActiveKapacitor}
addNew={{
url: `/sources/${source.id}/kapacitors/new`,
text: 'Add Kapacitor Connection',
}}
actions={[
{
icon: 'pencil',
text: 'edit',
handler: item => {
router.push(`${item.resource}/edit`)
},
},
{
icon: 'trash',
text: 'delete',
handler: item => {
handleDeleteKapacitor(item.kapacitor)
},
confirmable: true,
},
]}
selected={selected}
/>
</Authorized>
)
}
const InfluxTable = ({
source,
router,
sources,
location,
setActiveKapacitor,
handleDeleteSource,
handleDeleteKapacitor,
isUsingAuth,
me,
}) => {
return (
<div className="row">
<div className="col-md-12">
<div className="panel">
<div className="panel-heading">
<h2 className="panel-title">
{isUsingAuth ? (
<span>
Connections for <em>{me.currentOrganization.name}</em>
</span>
) : (
<span>Connections</span>
)}
</h2>
<Authorized requiredRole={EDITOR_ROLE}>
<Link
to={`/sources/${source.id}/manage-sources/new`}
className="btn btn-sm btn-primary"
>
<span className="icon plus" /> Add Connection
</Link>
</Authorized>
</div>
<div className="panel-body">
<table className="table v-center margin-bottom-zero table-highlight">
<thead>
<tr>
<th className="source-table--connect-col" />
<th>InfluxDB Connection</th>
<th className="text-right" />
<th>
Kapacitor Connection{' '}
<QuestionMarkTooltip
tipID="kapacitor-node-helper"
tipContent={
'<p>Kapacitor Connections are<br/>scoped per InfluxDB Connection.<br/>Only one can be active at a time.</p>'
}
/>
</th>
</tr>
</thead>
<tbody>
{sources.map(s => {
return (
<tr
key={s.id}
className={s.id === source.id ? 'highlight' : null}
>
<td>
{s.id === source.id ? (
<div className="btn btn-success btn-xs source-table--connect">
Connected
</div>
) : (
<Link
className="btn btn-default btn-xs source-table--connect"
to={`/sources/${s.id}/hosts`}
>
Connect
</Link>
)}
</td>
<td>
<h5 className="margin-zero">
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={
<strong>{s.name}</strong>
}
>
<Link
to={`${location.pathname}/${s.id}/edit`}
className={
s.id === source.id ? 'link-success' : null
}
>
<strong>{s.name}</strong>
{s.default ? ' (Default)' : null}
</Link>
</Authorized>
</h5>
<span>{s.url}</span>
</td>
<td className="text-right">
<Authorized requiredRole={EDITOR_ROLE}>
<ConfirmButton
customClass="delete-source table--show-on-row-hover"
type="btn-danger"
size="btn-xs"
text="Delete Connection"
confirmAction={handleDeleteSource(s)}
/>
</Authorized>
</td>
<td className="source-table--kapacitor">
{kapacitorDropdown(
s.kapacitors,
s,
router,
setActiveKapacitor,
handleDeleteKapacitor
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
)
}
const {array, bool, func, shape, string} = PropTypes
InfluxTable.propTypes = {
handleDeleteSource: func.isRequired,
location: shape({
pathname: string.isRequired,
}).isRequired,
router: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
source: shape({
id: string.isRequired,
links: shape({
proxy: string.isRequired,
self: string.isRequired,
}),
}),
sources: array.isRequired,
setActiveKapacitor: func.isRequired,
handleDeleteKapacitor: func.isRequired,
me: shape({
currentOrganization: shape({
id: string.isRequired,
name: string.isRequired,
}),
}),
isUsingAuth: bool,
}
const mapStateToProps = ({auth: {isUsingAuth, me}}) => ({isUsingAuth, me})
export default connect(mapStateToProps)(withRouter(InfluxTable))

View File

@ -0,0 +1,71 @@
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {SetActiveKapacitor, DeleteKapacitor} from 'src/shared/actions/sources'
import InfluxTableHead from 'src/sources/components/InfluxTableHead'
import InfluxTableHeader from 'src/sources/components/InfluxTableHeader'
import InfluxTableRow from 'src/sources/components/InfluxTableRow'
import {Source, Me} from 'src/types'
interface Props {
me: Me
source: Source
sources: Source[]
isUsingAuth: boolean
deleteKapacitor: DeleteKapacitor
setActiveKapacitor: SetActiveKapacitor
onDeleteSource: (source: Source) => () => void
}
class InfluxTable extends PureComponent<Props> {
public render() {
const {
source,
sources,
setActiveKapacitor,
onDeleteSource,
deleteKapacitor,
isUsingAuth,
me,
} = this.props
return (
<div className="row">
<div className="col-md-12">
<div className="panel">
<InfluxTableHeader
me={me}
source={source}
isUsingAuth={isUsingAuth}
/>
<div className="panel-body">
<table className="table v-center margin-bottom-zero table-highlight">
<InfluxTableHead />
<tbody>
{sources.map(s => {
return (
<InfluxTableRow
key={s.id}
source={s}
currentSource={source}
onDeleteSource={onDeleteSource}
deleteKapacitor={deleteKapacitor}
setActiveKapacitor={setActiveKapacitor}
/>
)
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = ({auth: {isUsingAuth, me}}) => ({isUsingAuth, me})
export default connect(mapStateToProps)(InfluxTable)

View File

@ -0,0 +1,28 @@
import React, {SFC, ReactElement} from 'react'
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
import {KAPACITOR_TOOLTIP_COPY} from 'src/sources/constants'
const InfluxTableHead: SFC<{}> = (): ReactElement<
HTMLTableHeaderCellElement
> => {
return (
<thead>
<tr>
<th className="source-table--connect-col" />
<th>InfluxDB Connection</th>
<th className="text-right" />
<th>
Kapacitor Connection
<QuestionMarkTooltip
tipID="kapacitor-node-helper"
tipContent={KAPACITOR_TOOLTIP_COPY}
/>
</th>
</tr>
</thead>
)
}
export default InfluxTableHead

View File

@ -0,0 +1,47 @@
import React, {PureComponent, ReactElement} from 'react'
import {Link} from 'react-router'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import {Me, Source} from 'src/types'
interface Props {
me: Me
source: Source
isUsingAuth: boolean
}
class InfluxTableHeader extends PureComponent<Props> {
public render() {
const {source} = this.props
return (
<div className="panel-heading">
<h2 className="panel-title">{this.title}</h2>
<Authorized requiredRole={EDITOR_ROLE}>
<Link
to={`/sources/${source.id}/manage-sources/new`}
className="btn btn-sm btn-primary"
>
<span className="icon plus" /> Add Connection
</Link>
</Authorized>
</div>
)
}
private get title(): ReactElement<HTMLSpanElement> {
const {isUsingAuth, me} = this.props
if (isUsingAuth) {
return (
<span>
Connections for <em>{me.currentOrganization.name}</em>
</span>
)
}
return <span>Connections</span>
}
}
export default InfluxTableHeader

View File

@ -0,0 +1,98 @@
import React, {PureComponent, ReactElement} from 'react'
import {Link} from 'react-router'
import * as actions from 'src/shared/actions/sources'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import ConfirmButton from 'src/shared/components/ConfirmButton'
import KapacitorDropdown from 'src/sources/components/KapacitorDropdown'
import ConnectionLink from 'src/sources/components/ConnectionLink'
import {Source} from 'src/types'
interface Props {
source: Source
currentSource: Source
onDeleteSource: (source: Source) => void
setActiveKapacitor: actions.SetActiveKapacitor
deleteKapacitor: actions.DeleteKapacitor
}
class InfluxTableRow extends PureComponent<Props> {
public render() {
const {
source,
currentSource,
setActiveKapacitor,
deleteKapacitor,
} = this.props
return (
<tr className={this.className}>
<td>{this.connectButton}</td>
<td>
<ConnectionLink source={source} currentSource={currentSource} />
<span>{source.url}</span>
</td>
<td className="text-right">
<Authorized requiredRole={EDITOR_ROLE}>
<ConfirmButton
type="btn-danger"
size="btn-xs"
text="Delete Connection"
confirmAction={this.handleDeleteSource}
customClass="delete-source table--show-on-row-hover"
/>
</Authorized>
</td>
<td className="source-table--kapacitor">
<KapacitorDropdown
source={source}
kapacitors={source.kapacitors}
deleteKapacitor={deleteKapacitor}
setActiveKapacitor={setActiveKapacitor}
/>
</td>
</tr>
)
}
private handleDeleteSource = (): void => {
this.props.onDeleteSource(this.props.source)
}
private get connectButton(): ReactElement<HTMLDivElement> {
const {source} = this.props
if (this.isCurrentSource) {
return (
<div className="btn btn-success btn-xs source-table--connect">
Connected
</div>
)
}
return (
<Link
className="btn btn-default btn-xs source-table--connect"
to={`/sources/${source.id}/hosts`}
>
Connect
</Link>
)
}
private get className(): string {
if (this.isCurrentSource) {
return 'hightlight'
}
return ''
}
private get isCurrentSource(): boolean {
const {source, currentSource} = this.props
return source.id === currentSource.id
}
}
export default InfluxTableRow

View File

@ -0,0 +1,118 @@
import React, {PureComponent, ReactElement} from 'react'
import {Link, withRouter, RouteComponentProps} from 'react-router'
import Dropdown from 'src/shared/components/Dropdown'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import {Source, Kapacitor} from 'src/types'
import {SetActiveKapacitor} from 'src/shared/actions/sources'
interface Props {
source: Source
kapacitors: Kapacitor[]
setActiveKapacitor: SetActiveKapacitor
deleteKapacitor: (Kapacitor: Kapacitor) => void
}
interface KapacitorItem {
text: string
resource: string
kapacitor: Kapacitor
}
class KapacitorDropdown extends PureComponent<
Props & RouteComponentProps<any, any>
> {
public render() {
const {source, router, setActiveKapacitor, deleteKapacitor} = this.props
if (this.isKapacitorsEmpty) {
return (
<Authorized requiredRole={EDITOR_ROLE}>
<Link
to={`/sources/${source.id}/kapacitors/new`}
className="btn btn-xs btn-default"
>
<span className="icon plus" /> Add Kapacitor Connection
</Link>
</Authorized>
)
}
return (
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={this.UnauthorizedDropdown}
>
<Dropdown
className="dropdown-260"
buttonColor="btn-primary"
buttonSize="btn-xs"
items={this.kapacitorItems}
onChoose={setActiveKapacitor}
addNew={{
url: `/sources/${source.id}/kapacitors/new`,
text: 'Add Kapacitor Connection',
}}
actions={[
{
icon: 'pencil',
text: 'edit',
handler: item => {
router.push(`${item.resource}/edit`)
},
},
{
icon: 'trash',
text: 'delete',
handler: item => {
deleteKapacitor(item.kapacitor)
},
confirmable: true,
},
]}
selected={this.selected}
/>
</Authorized>
)
}
private get UnauthorizedDropdown(): ReactElement<HTMLDivElement> {
return (
<div className="source-table--kapacitor__view-only">{this.selected}</div>
)
}
private get isKapacitorsEmpty(): boolean {
const {kapacitors} = this.props
return !kapacitors || kapacitors.length === 0
}
private get kapacitorItems(): KapacitorItem[] {
const {kapacitors, source} = this.props
return kapacitors.map(k => {
return {
text: k.name,
resource: `/sources/${source.id}/kapacitors/${k.id}`,
kapacitor: k,
}
})
}
private get activeKapacitor(): Kapacitor {
return this.props.kapacitors.find(k => k.active)
}
private get selected(): string {
let selected = ''
if (this.activeKapacitor) {
selected = this.activeKapacitor.name
} else {
selected = this.kapacitorItems[0].text
}
return selected
}
}
export default withRouter<Props>(KapacitorDropdown)

View File

@ -1,2 +0,0 @@
export const REQUIRED_ROLE_COPY =
'The minimum Role a user must have<br />in order to access this source.'

View File

@ -0,0 +1,5 @@
export const REQUIRED_ROLE_COPY =
'The minimum Role a user must have<br />in order to access this source.'
export const KAPACITOR_TOOLTIP_COPY =
'<p>Kapacitor Connections are<br/>scoped per InfluxDB Connection.<br/>Only one can be active at a time.</p>'

View File

@ -65,8 +65,8 @@ class ManageSources extends PureComponent<Props> {
<InfluxTable
source={source}
sources={sources}
handleDeleteKapacitor={deleteKapacitor}
handleDeleteSource={this.handleDeleteSource}
deleteKapacitor={deleteKapacitor}
onDeleteSource={this.handleDeleteSource}
setActiveKapacitor={this.handleSetActiveKapacitor}
/>
<p className="version-number">Chronograf Version: {VERSION}</p>