Allow users to import Dashboard from file
parent
0a72f12b7e
commit
588295e0cd
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -134,3 +134,8 @@ export interface Dashboard {
|
|||
organization: string
|
||||
links?: DashboardLinks
|
||||
}
|
||||
|
||||
export interface DashboardFile {
|
||||
chronografVersion?: string
|
||||
dashboard: Dashboard
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue