diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index a4be153f0..0bff3b188 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -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)) + } +} diff --git a/ui/src/dashboards/components/DashboardsPageContents.tsx b/ui/src/dashboards/components/DashboardsPageContents.tsx index 61ae99509..5e908fac9 100644 --- a/ui/src/dashboards/components/DashboardsPageContents.tsx +++ b/ui/src/dashboards/components/DashboardsPageContents.tsx @@ -23,6 +23,7 @@ interface Props { dashboard: Dashboard ) => (event: MouseEvent) => void onExportDashboard: (dashboard: Dashboard) => () => void + onImportDashboard: (dashboard: Dashboard) => void showOverlay: ShowOverlay dashboardLink: string } @@ -131,7 +132,7 @@ class DashboardsPageContents extends Component { } 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 { showOverlay( {({onDismissOverlay}) => ( - + )} , options diff --git a/ui/src/dashboards/components/ImportDashboardOverlay.tsx b/ui/src/dashboards/components/ImportDashboardOverlay.tsx index 412ad0d04..0d2fa1be6 100644 --- a/ui/src/dashboards/components/ImportDashboardOverlay.tsx +++ b/ui/src/dashboards/components/ImportDashboardOverlay.tsx @@ -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 { +interface State { + dashboardFromFile: Dashboard +} + +class ImportDashboardOverlay extends PureComponent { + constructor(props: Props) { + super(props) + + this.state = { + dashboardFromFile: null, + } + } + public render() { const {onDismissOverlay} = this.props @@ -15,11 +33,37 @@ class ImportDashboardOverlay extends PureComponent { -

sweeeet

+ +
) } + + private handleChooseFile = (e: ChangeEvent): void => { + const file = e.target.files[0] + const fileReader = new FileReader() + fileReader.onloadend = () => { + const result: DashboardFile = JSON.parse(fileReader.result) + const dashboard = getDeep(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 diff --git a/ui/src/dashboards/containers/DashboardsPage.tsx b/ui/src/dashboards/containers/DashboardsPage.tsx index 2a79833d2..9ff917b84 100644 --- a/ui/src/dashboards/containers/DashboardsPage.tsx +++ b/ui/src/dashboards/containers/DashboardsPage.tsx @@ -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 { onCreateDashboard={this.handleCreateDashboard} onCloneDashboard={this.handleCloneDashboard} onExportDashboard={this.handleExportDashboard} + onImportDashboard={this.handleImportDashboard} /> ) @@ -92,10 +96,21 @@ class DashboardsPage extends PureComponent { ) } - private modifyDashboardForDownload = async (dashboard: Dashboard) => { + private modifyDashboardForDownload = async ( + dashboard: Dashboard + ): Promise => { const version = await this.props.handleGetChronografVersion() return {chronografVersion: version, dashboard} } + + private handleImportDashboard = async ( + dashboard: Dashboard + ): Promise => { + 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( diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index a713a119a..6ca5cbe1d 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -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 = { diff --git a/ui/src/types/dashboard.ts b/ui/src/types/dashboard.ts index ab7d15e0a..838064fcd 100644 --- a/ui/src/types/dashboard.ts +++ b/ui/src/types/dashboard.ts @@ -134,3 +134,8 @@ export interface Dashboard { organization: string links?: DashboardLinks } + +export interface DashboardFile { + chronografVersion?: string + dashboard: Dashboard +}