Skip to content

Commit

Permalink
Dashboard save button (apache#4979)
Browse files Browse the repository at this point in the history
* save button

* fix slices list height

* save custom css

* merge save-dash changes from dashboard v1
apache#4900
apache#5051
  • Loading branch information
Grace Guo authored and williaster committed Jun 22, 2018
1 parent 82ab75e commit ae37277
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 112 deletions.
43 changes: 41 additions & 2 deletions superset/assets/src/dashboard/actions/dashboardState.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ import { addChart, removeChart, refreshChart } from '../../chart/chartAction';
import { chart as initChart } from '../../chart/chartReducer';
import { fetchDatasourceMetadata } from '../../dashboard/actions/datasources';
import { applyDefaultFormData } from '../../explore/stores/store';
import { addWarningToast } from './messageToasts';
import { getAjaxErrorMsg } from '../../modules/utils';
import { SAVE_TYPE_OVERWRITE } from '../util/constants';
import { t } from '../../locales';

import {
addSuccessToast,
addWarningToast,
addDangerToast,
} from './messageToasts';

export const SET_UNSAVED_CHANGES = 'SET_UNSAVED_CHANGES';
export function setUnsavedChanges(hasUnsavedChanges) {
Expand Down Expand Up @@ -66,6 +74,11 @@ export function toggleExpandSlice(sliceId) {
return { type: TOGGLE_EXPAND_SLICE, sliceId };
}

export const UPDATE_CSS = 'UPDATE_CSS';
export function updateCss(css) {
return { type: UPDATE_CSS, css };
}

export const SET_EDIT_MODE = 'SET_EDIT_MODE';
export function setEditMode(editMode) {
return { type: SET_EDIT_MODE, editMode };
Expand All @@ -81,14 +94,40 @@ export function onSave() {
return { type: ON_SAVE };
}

export function saveDashboard() {
export function saveDashboardRequestSuccess() {
return dispatch => {
dispatch(onSave());
// clear layout undo history
dispatch(UndoActionCreators.clearHistory());
};
}

export function saveDashboardRequest(data, id, saveType) {
const path = saveType === SAVE_TYPE_OVERWRITE ? 'save_dash' : 'copy_dash';
const url = `/superset/${path}/${id}/`;
return dispatch =>
$.ajax({
type: 'POST',
url,
data: {
data: JSON.stringify(data),
},
success: () => {
dispatch(saveDashboardRequestSuccess());
dispatch(addSuccessToast(t('This dashboard was saved successfully.')));
},
error: error => {
const errorMsg = getAjaxErrorMsg(error);
dispatch(
addDangerToast(
`${t('Sorry, there was an error saving this dashboard: ')}
${errorMsg}`,
),
);
},
});
}

export function fetchCharts(chartList = [], force = false, interval = 0) {
return (dispatch, getState) => {
const timeout = getState().dashboardInfo.common.conf
Expand Down
38 changes: 15 additions & 23 deletions superset/assets/src/dashboard/components/Controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import PropTypes from 'prop-types';
import $ from 'jquery';
import { DropdownButton, MenuItem } from 'react-bootstrap';

import CssEditor from './CssEditor';
import RefreshIntervalModal from './RefreshIntervalModal';
import SaveModal from './SaveModal';
import { t } from '../../locales';

function updateDom(css) {
Expand All @@ -31,12 +31,10 @@ const propTypes = {
addDangerToast: PropTypes.func.isRequired,
dashboardInfo: PropTypes.object.isRequired,
dashboardTitle: PropTypes.string.isRequired,
layout: PropTypes.object.isRequired,
filters: PropTypes.object.isRequired,
expandedSlices: PropTypes.object.isRequired,
css: PropTypes.string.isRequired,
slices: PropTypes.array,
onSave: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
updateCss: PropTypes.func.isRequired,
forceRefreshAllCharts: PropTypes.func.isRequired,
startPeriodicRender: PropTypes.func.isRequired,
editMode: PropTypes.bool,
Expand All @@ -51,9 +49,11 @@ class Controls extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
css: '',
css: props.css,
cssTemplates: [],
};

this.changeCss = this.changeCss.bind(this);
}

componentWillMount() {
Expand All @@ -74,17 +74,14 @@ class Controls extends React.PureComponent {
updateDom(css);
});
this.props.onChange();
this.props.updateCss(css);
}

render() {
const {
dashboardTitle,
layout,
filters,
expandedSlices,
startPeriodicRender,
forceRefreshAllCharts,
onSave,
editMode,
} = this.props;

Expand All @@ -110,19 +107,6 @@ class Controls extends React.PureComponent {
}
triggerNode={<span>{t('Set auto-refresh interval')}</span>}
/>
<SaveModal
addSuccessToast={this.props.addSuccessToast}
addDangerToast={this.props.addDangerToast}
dashboardId={this.props.dashboardInfo.id}
dashboardTitle={dashboardTitle}
layout={layout}
filters={filters}
expandedSlices={expandedSlices}
onSave={onSave}
css={this.state.css}
triggerNode={<span>{editMode ? t('Save') : t('Save as')}</span>}
isMenuItem
/>
{editMode && (
<MenuItem
target="_blank"
Expand All @@ -134,6 +118,14 @@ class Controls extends React.PureComponent {
{editMode && (
<MenuItem href={emailLink}>{t('Email dashboard link')}</MenuItem>
)}
{editMode && (
<CssEditor
triggerNode={<span>{t('Edit CSS')}</span>}
initialCss={this.state.css}
templates={this.state.cssTemplates}
onChange={this.changeCss}
/>
)}
</DropdownButton>
</span>
);
Expand Down
98 changes: 80 additions & 18 deletions superset/assets/src/dashboard/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/* eslint-env browser */
import React from 'react';
import PropTypes from 'prop-types';
import { ButtonGroup, ButtonToolbar } from 'react-bootstrap';
import {
DropdownButton,
MenuItem,
ButtonGroup,
ButtonToolbar,
} from 'react-bootstrap';

import Controls from './Controls';
import EditableTitle from '../../components/EditableTitle';
Expand All @@ -9,7 +15,11 @@ import FaveStar from '../../components/FaveStar';
import SaveModal from './SaveModal';
import { chartPropShape } from '../util/propShapes';
import { t } from '../../locales';
import { UNDO_LIMIT } from '../util/constants';
import {
UNDO_LIMIT,
SAVE_TYPE_NEWDASHBOARD,
SAVE_TYPE_OVERWRITE,
} from '../util/constants';

const propTypes = {
addSuccessToast: PropTypes.func.isRequired,
Expand All @@ -20,6 +30,7 @@ const propTypes = {
layout: PropTypes.object.isRequired,
filters: PropTypes.object.isRequired,
expandedSlices: PropTypes.object.isRequired,
css: PropTypes.string.isRequired,
isStarred: PropTypes.bool.isRequired,
onSave: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
Expand All @@ -32,6 +43,7 @@ const propTypes = {
setEditMode: PropTypes.func.isRequired,
showBuilderPane: PropTypes.bool.isRequired,
toggleBuilderPane: PropTypes.func.isRequired,
updateCss: PropTypes.func.isRequired,
hasUnsavedChanges: PropTypes.bool.isRequired,
maxUndoHistoryExceeded: PropTypes.bool.isRequired,

Expand All @@ -45,6 +57,10 @@ const propTypes = {
};

class Header extends React.PureComponent {
static discardChanges() {
window.location.reload();
}

constructor(props) {
super(props);
this.state = {
Expand All @@ -54,6 +70,7 @@ class Header extends React.PureComponent {
this.handleChangeText = this.handleChangeText.bind(this);
this.toggleEditMode = this.toggleEditMode.bind(this);
this.forceRefresh = this.forceRefresh.bind(this);
this.overwriteDashboard = this.overwriteDashboard.bind(this);
}

componentWillReceiveProps(nextProps) {
Expand Down Expand Up @@ -88,46 +105,70 @@ class Header extends React.PureComponent {
this.props.setEditMode(!this.props.editMode);
}

overwriteDashboard() {
const {
dashboardTitle,
layout: positions,
expandedSlices,
css,
filters,
dashboardInfo,
} = this.props;

const data = {
positions,
expanded_slices: expandedSlices,
css,
dashboard_title: dashboardTitle,
default_filters: JSON.stringify(filters),
};

this.props.onSave(data, dashboardInfo.id, SAVE_TYPE_OVERWRITE);
}

render() {
const {
dashboardTitle,
layout,
filters,
expandedSlices,
css,
onUndo,
onRedo,
undoLength,
redoLength,
onChange,
onSave,
updateCss,
editMode,
showBuilderPane,
dashboardInfo,
hasUnsavedChanges,
} = this.props;

const userCanEdit = dashboardInfo.dash_save_perm;
const userCanEdit = dashboardInfo.dash_edit_perm;
const userCanSaveAs = dashboardInfo.dash_save_perm;

return (
<div className="dashboard-header">
<div className="dashboard-component-header header-large">
<EditableTitle
title={dashboardTitle}
canEdit={this.props.dashboardInfo.dash_save_perm && editMode}
canEdit={userCanEdit && editMode}
onSaveTitle={this.handleChangeText}
showTooltip={false}
/>
<span className="favstar m-l-5">
<FaveStar
itemId={this.props.dashboardInfo.id}
itemId={dashboardInfo.id}
fetchFaveStar={this.props.fetchFaveStar}
saveFaveStar={this.props.saveFaveStar}
isStarred={this.props.isStarred}
/>
</span>
</div>
<ButtonToolbar>
{userCanEdit && (
{userCanSaveAs && (
<ButtonGroup>
{editMode && (
<Button
Expand Down Expand Up @@ -161,44 +202,65 @@ class Header extends React.PureComponent {
<Button
bsSize="small"
onClick={this.toggleEditMode}
bsStyle={editMode ? undefined : 'primary'}
bsStyle={hasUnsavedChanges ? 'primary' : undefined}
disabled={!userCanEdit}
>
{editMode ? t('Switch to View Mode') : t('Edit Dashboard')}
{editMode ? t('Switch to view mode') : t('Edit dashboard')}
</Button>
) : (
<Button
bsSize="small"
bsStyle={hasUnsavedChanges ? 'primary' : undefined}
onClick={this.overwriteDashboard}
>
{t('Save changes')}
</Button>
)}
<DropdownButton
title=""
id="save-dash-split-button"
bsStyle={hasUnsavedChanges ? 'primary' : undefined}
bsSize="small"
pullRight
>
<SaveModal
addSuccessToast={this.props.addSuccessToast}
addDangerToast={this.props.addDangerToast}
dashboardId={this.props.dashboardInfo.id}
dashboardId={dashboardInfo.id}
dashboardTitle={dashboardTitle}
saveType={SAVE_TYPE_NEWDASHBOARD}
layout={layout}
filters={filters}
expandedSlices={expandedSlices}
css={css}
onSave={onSave}
// @TODO need to figure out css
css=""
triggerNode={
<Button bsStyle="primary" bsSize="small">
{t('Save changes')}
</Button>
}
isMenuItem
triggerNode={<span>{t('Save as')}</span>}
canOverwrite={userCanEdit}
/>
)}
{hasUnsavedChanges && (
<MenuItem eventKey="discard" onSelect={Header.discardChanges}>
{t('Discard changes')}
</MenuItem>
)}
</DropdownButton>
</ButtonGroup>
)}

<Controls
addSuccessToast={this.props.addSuccessToast}
addDangerToast={this.props.addDangerToast}
dashboardInfo={this.props.dashboardInfo}
dashboardInfo={dashboardInfo}
dashboardTitle={dashboardTitle}
layout={layout}
filters={filters}
expandedSlices={expandedSlices}
css={css}
onSave={onSave}
onChange={onChange}
forceRefreshAllCharts={this.forceRefresh}
startPeriodicRender={this.props.startPeriodicRender}
updateCss={updateCss}
editMode={editMode}
/>
</ButtonToolbar>
Expand Down
Loading

0 comments on commit ae37277

Please sign in to comment.