Allow users to import Dashboard from file

pull/10616/head
Iris Scholten 2018-05-30 15:40:00 -07:00
parent 0a72f12b7e
commit 588295e0cd
6 changed files with 119 additions and 7 deletions

View File

@ -8,6 +8,7 @@ import {
addDashboardCell as addDashboardCellAJAX,
deleteDashboardCell as deleteDashboardCellAJAX,
runTemplateVariableQuery,
createDashboard as createDashboardAJAX,
} from 'src/dashboards/apis'
import {getMe} from 'src/shared/apis/auth'
@ -77,6 +78,13 @@ export const updateDashboard = dashboard => ({
},
})
export const createDashboard = dashboard => ({
type: 'CREATE_DASHBOARD',
payload: {
dashboard,
},
})
export const deleteDashboard = dashboard => ({
type: 'DELETE_DASHBOARD',
payload: {
@ -375,3 +383,30 @@ export const updateTempVarValues = (source, dashboard) => async dispatch => {
dispatch(errorThrown(error))
}
}
export const importDashboardAsync = dashboard => async dispatch => {
try {
// save only selected template values to server
const templatesWithOnlySelectedValues = removeUnselectedTemplateValues(
dashboard
)
const {
data: dashboardWithOnlySelectedTemplateValues,
} = await createDashboardAJAX({
...dashboard,
templates: templatesWithOnlySelectedValues,
})
// save all template values to redux
dispatch(
createDashboard({
...dashboardWithOnlySelectedTemplateValues,
templates: dashboard.templates,
})
)
} catch (error) {
console.error(error)
dispatch(errorThrown(error))
}
}

View File

@ -23,6 +23,7 @@ interface Props {
dashboard: Dashboard
) => (event: MouseEvent<HTMLButtonElement>) => void
onExportDashboard: (dashboard: Dashboard) => () => void
onImportDashboard: (dashboard: Dashboard) => void
showOverlay: ShowOverlay
dashboardLink: string
}
@ -131,7 +132,7 @@ class DashboardsPageContents extends Component<Props, State> {
}
private showImportOverlay = (): void => {
const {showOverlay} = this.props
const {showOverlay, onImportDashboard} = this.props
const options = {
dismissOnClickOutside: false,
dismissOnEscape: false,
@ -140,7 +141,10 @@ class DashboardsPageContents extends Component<Props, State> {
showOverlay(
<OverlayContext.Consumer>
{({onDismissOverlay}) => (
<ImportDashboardOverlay onDismissOverlay={onDismissOverlay} />
<ImportDashboardOverlay
onDismissOverlay={onDismissOverlay}
onImportDashboard={onImportDashboard}
/>
)}
</OverlayContext.Consumer>,
options

View File

@ -1,13 +1,31 @@
import React, {PureComponent} from 'react'
import React, {PureComponent, ChangeEvent} from 'react'
import {getDeep} from 'src/utils/wrappers'
import Container from 'src/shared/components/overlay/OverlayContainer'
import Heading from 'src/shared/components/overlay/OverlayHeading'
import Body from 'src/shared/components/overlay/OverlayBody'
import {Dashboard} from 'src/types'
import {DashboardFile} from 'src/types/dashboard'
interface Props {
onDismissOverlay: () => void
onImportDashboard: (dashboard: Dashboard) => void
}
class ImportDashboardOverlay extends PureComponent<Props> {
interface State {
dashboardFromFile: Dashboard
}
class ImportDashboardOverlay extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
dashboardFromFile: null,
}
}
public render() {
const {onDismissOverlay} = this.props
@ -15,11 +33,37 @@ class ImportDashboardOverlay extends PureComponent<Props> {
<Container maxWidth={800}>
<Heading title="Import Dashboard" onDismiss={onDismissOverlay} />
<Body>
<p>sweeeet</p>
<input type="file" onChange={this.handleChooseFile} accept=".json" />
<button
className="btn btn-sm btn-default"
onClick={this.handleImportDashboard}
>
Import
</button>
</Body>
</Container>
)
}
private handleChooseFile = (e: ChangeEvent<HTMLInputElement>): void => {
const file = e.target.files[0]
const fileReader = new FileReader()
fileReader.onloadend = () => {
const result: DashboardFile = JSON.parse(fileReader.result)
const dashboard = getDeep<Dashboard>(result, 'dashboard', null)
this.setState({dashboardFromFile: dashboard})
}
fileReader.readAsText(file)
}
private handleImportDashboard = (): void => {
const {onImportDashboard, onDismissOverlay} = this.props
const {dashboardFromFile} = this.state
if (dashboardFromFile) {
onImportDashboard(dashboardFromFile)
}
onDismissOverlay()
}
}
export default ImportDashboardOverlay

View File

@ -11,19 +11,22 @@ import {
getDashboardsAsync,
deleteDashboardAsync,
getChronografVersion,
importDashboardAsync,
} from 'src/dashboards/actions'
import {NEW_DASHBOARD} from 'src/dashboards/constants'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Source, Dashboard} from 'src/types'
import {DashboardFile} from 'src/types/dashboard'
interface Props {
source: Source
router: InjectedRouter
handleGetDashboards: () => void
handleGetChronografVersion: () => void
handleGetChronografVersion: () => string
handleDeleteDashboard: (dashboard: Dashboard) => void
handleImportDashboard: (dashboard: Dashboard) => void
dashboards: Dashboard[]
}
@ -47,6 +50,7 @@ class DashboardsPage extends PureComponent<Props> {
onCreateDashboard={this.handleCreateDashboard}
onCloneDashboard={this.handleCloneDashboard}
onExportDashboard={this.handleExportDashboard}
onImportDashboard={this.handleImportDashboard}
/>
</div>
)
@ -92,10 +96,21 @@ class DashboardsPage extends PureComponent<Props> {
)
}
private modifyDashboardForDownload = async (dashboard: Dashboard) => {
private modifyDashboardForDownload = async (
dashboard: Dashboard
): Promise<DashboardFile> => {
const version = await this.props.handleGetChronografVersion()
return {chronografVersion: version, dashboard}
}
private handleImportDashboard = async (
dashboard: Dashboard
): Promise<void> => {
await this.props.handleImportDashboard({
...dashboard,
name: `${dashboard.name} (imported)`,
})
}
}
const mapStateToProps = ({dashboardUI: {dashboards, dashboard}}) => ({
@ -107,6 +122,7 @@ const mapDispatchToProps = {
handleGetDashboards: getDashboardsAsync,
handleDeleteDashboard: deleteDashboardAsync,
handleGetChronografVersion: getChronografVersion,
handleImportDashboard: importDashboardAsync,
}
export default withRouter(

View File

@ -46,6 +46,14 @@ export default function ui(state = initialState, action) {
return {...state, ...newState}
}
case 'CREATE_DASHBOARD': {
const {dashboard} = action.payload
const newState = {
dashboards: [...state.dashboards, dashboard],
}
return {...state, ...newState}
}
case 'DELETE_DASHBOARD': {
const {dashboard} = action.payload
const newState = {

View File

@ -134,3 +134,8 @@ export interface Dashboard {
organization: string
links?: DashboardLinks
}
export interface DashboardFile {
chronografVersion?: string
dashboard: Dashboard
}