Add clone dashboard functionality to orgs dashboard list (#11265)

Co-Authored-By: Deniz Kusefoglu <deniz@influxdata.com>
pull/11259/head
alexpaxton 2019-01-17 16:37:25 -08:00 committed by GitHub
parent a2435354bb
commit be430667a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 19 deletions

View File

@ -13,6 +13,7 @@ import {
ConfirmationButton,
Stack,
Label,
ComponentColor,
} from 'src/clockface'
import EditableDescription from 'src/shared/components/editable_description/EditableDescription'
@ -72,6 +73,7 @@ export default class DashboardsIndexTableRow extends PureComponent<Props> {
<ComponentSpacer align={Alignment.Left} stackChildren={Stack.Columns}>
<Button
size={ComponentSize.ExtraSmall}
color={ComponentColor.Secondary}
text="Clone"
icon={IconFont.Duplicate}
onClick={this.handleClone}

View File

@ -1,26 +1,51 @@
// Libraries
import React, {PureComponent} from 'react'
import {InjectedRouter} from 'react-router'
import {connect} from 'react-redux'
// Components
import {IndexList} from 'src/clockface'
import DashboardRow from 'src/organizations/components/DashboardRow'
// APIs
import {createDashboard} from 'src/dashboards/apis/v2'
// Constants
import {dashboardCreateFailed} from 'src/shared/copy/notifications'
// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
// Types
import {Notification} from 'src/types/notifications'
import {Dashboard} from 'src/types/v2'
interface Props {
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface DispatchProps {
notify: (message: Notification) => void
}
interface OwnProps {
router: InjectedRouter
orgID: string
dashboards: Dashboard[]
emptyState: JSX.Element
onDeleteDashboard: (dashboard: Dashboard) => void
}
export default class DashboardList extends PureComponent<Props> {
type Props = DispatchProps & OwnProps
@ErrorHandling
class DashboardList extends PureComponent<Props> {
public render() {
return (
<IndexList>
<IndexList.Header>
<IndexList.HeaderCell columnName="name" width="50%" />
<IndexList.HeaderCell columnName="modified" width="50%" />
<IndexList.HeaderCell columnName="Name" width="50%" />
<IndexList.HeaderCell columnName="Modified" width="25%" />
<IndexList.HeaderCell columnName="" width="25%" />
</IndexList.Header>
<IndexList.Body columnCount={2} emptyState={this.props.emptyState}>
{this.rows}
@ -29,6 +54,24 @@ export default class DashboardList extends PureComponent<Props> {
)
}
private handleCloneDashboard = async (
dashboard: Dashboard
): Promise<void> => {
const {router, notify, orgID} = this.props
const name = `${dashboard.name} (clone)`
try {
const data = await createDashboard({
...dashboard,
name,
orgID,
})
router.push(`/dashboards/${data.id}`)
} catch (error) {
notify(dashboardCreateFailed())
}
}
private get rows(): JSX.Element[] {
const {onDeleteDashboard} = this.props
@ -37,7 +80,17 @@ export default class DashboardList extends PureComponent<Props> {
dashboard={d}
key={d.id}
onDeleteDashboard={onDeleteDashboard}
onCloneDashboard={this.handleCloneDashboard}
/>
))
}
}
const mdtp: DispatchProps = {
notify: notifyAction,
}
export default connect<null, DispatchProps, OwnProps>(
null,
mdtp
)(DashboardList)

View File

@ -4,8 +4,17 @@ import {Link} from 'react-router'
import moment from 'moment'
// Components
import {IndexList, Alignment, ComponentSize} from 'src/clockface'
import ConfirmationButton from 'src/clockface/components/confirmation_button/ConfirmationButton'
import {
IndexList,
Alignment,
ComponentSize,
ConfirmationButton,
ComponentSpacer,
Stack,
Button,
IconFont,
ComponentColor,
} from 'src/clockface'
// Types
import {Dashboard} from 'src/types/v2'
@ -16,6 +25,7 @@ import {UPDATED_AT_TIME_FORMAT} from 'src/dashboards/constants'
interface Props {
dashboard: Dashboard
onDeleteDashboard: (dashboard: Dashboard) => void
onCloneDashboard: (dashboard: Dashboard) => void
}
export default class DashboardRow extends PureComponent<Props> {
@ -27,19 +37,48 @@ export default class DashboardRow extends PureComponent<Props> {
<IndexList.Cell>
<Link to={`/dashboards/${dashboard.id}`}>{dashboard.name}</Link>
</IndexList.Cell>
<IndexList.Cell revealOnHover={true}>
{moment(dashboard.meta.updatedAt).format(UPDATED_AT_TIME_FORMAT)}
</IndexList.Cell>
{this.lastModifiedCell}
<IndexList.Cell revealOnHover={true} alignment={Alignment.Right}>
<ConfirmationButton
size={ComponentSize.ExtraSmall}
text="Delete"
confirmText="Confirm"
onConfirm={onDeleteDashboard}
returnValue={dashboard}
/>
<ComponentSpacer stackChildren={Stack.Columns} align={Alignment.Left}>
<Button
size={ComponentSize.ExtraSmall}
color={ComponentColor.Secondary}
text="Clone"
icon={IconFont.Duplicate}
titleText="Create a duplicate copy of this Dashboard"
onClick={this.handleCloneDashboard}
/>
<ConfirmationButton
size={ComponentSize.ExtraSmall}
text="Delete"
confirmText="Confirm"
onConfirm={onDeleteDashboard}
returnValue={dashboard}
/>
</ComponentSpacer>
</IndexList.Cell>
</IndexList.Row>
)
}
private handleCloneDashboard = (): void => {
const {dashboard, onCloneDashboard} = this.props
onCloneDashboard(dashboard)
}
private get lastModifiedCell(): JSX.Element {
const {dashboard} = this.props
const relativeTimestamp = moment(dashboard.meta.updatedAt).fromNow()
const absoluteTimestamp = moment(dashboard.meta.updatedAt).format(
UPDATED_AT_TIME_FORMAT
)
return (
<IndexList.Cell>
<span title={absoluteTimestamp}>{relativeTimestamp}</span>
</IndexList.Cell>
)
}
}

View File

@ -1,5 +1,6 @@
// Libraries
import React, {PureComponent, ChangeEvent} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
import _ from 'lodash'
// APIs
@ -17,18 +18,21 @@ import {Dashboard} from 'src/types/v2'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
interface OwnProps {
dashboards: Dashboard[]
orgName: string
orgID: string
onChange: () => void
}
type Props = OwnProps & WithRouterProps
interface State {
searchTerm: string
}
@ErrorHandling
export default class Dashboards extends PureComponent<Props, State> {
class Dashboards extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
@ -38,7 +42,7 @@ export default class Dashboards extends PureComponent<Props, State> {
public render() {
const {searchTerm} = this.state
const {dashboards} = this.props
const {dashboards, orgID, router} = this.props
return (
<>
@ -62,6 +66,8 @@ export default class Dashboards extends PureComponent<Props, State> {
dashboards={ds}
emptyState={this.emptyState}
onDeleteDashboard={this.handleDeleteDashboard}
orgID={orgID}
router={router}
/>
)}
</FilterList>
@ -104,3 +110,5 @@ export default class Dashboards extends PureComponent<Props, State> {
)
}
}
export default withRouter<OwnProps>(Dashboards)

View File

@ -131,6 +131,7 @@ class OrganizationView extends PureComponent<Props> {
dashboards={dashboards}
orgName={org.name}
onChange={fetch}
orgID={org.id}
/>
</Spinner>
)}