Added Alert for ControlPanel and ChartContainer (#1626)

* Added Alert for ControlPanel and ChartContainer

Done:
 - Add alert for Control Panel when fetch_datasource_metadata failes
 - Add alert for Chart Container when update_explore query fails

* Changed color to warning-yellow

* Solve linter issue

* Fixed indent and delete error_redirect
This commit is contained in:
vera-liu 2016-11-18 11:17:06 -08:00 committed by GitHub
parent 0acf26b37c
commit a8480f5492
6 changed files with 79 additions and 22 deletions

View File

@ -24,8 +24,8 @@ export function fetchSucceeded() {
}
export const FETCH_FAILED = 'FETCH_FAILED';
export function fetchFailed() {
return { type: FETCH_FAILED };
export function fetchFailed(error) {
return { type: FETCH_FAILED, error };
}
export function fetchFieldOptions(datasourceId, datasourceType) {
@ -35,18 +35,19 @@ export function fetchFieldOptions(datasourceId, datasourceType) {
if (datasourceId) {
const params = [`datasource_id=${datasourceId}`, `datasource_type=${datasourceType}`];
const url = '/superset/fetch_datasource_metadata?' + params.join('&');
$.get(url, (data, status) => {
if (status === 'success') {
// populate options for select type fields
$.ajax({
type: 'GET',
url,
success: (data) => {
dispatch(setFieldOptions(data.field_options));
dispatch(fetchSucceeded());
} else if (status === 'error') {
dispatch(fetchFailed());
}
},
error(error) {
dispatch(fetchFailed(error.responseJSON.error));
},
});
} else {
// in what case don't we have a datasource id?
dispatch(fetchFailed('Please select a datasource'));
}
};
}
@ -119,8 +120,8 @@ export function chartUpdateStarted() {
}
export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED ';
export function chartUpdateFailed() {
return { type: CHART_UPDATE_FAILED };
export function chartUpdateFailed(error) {
return { type: CHART_UPDATE_FAILED, error };
}
export function updateExplore(datasource_type, datasource_id, form_data) {
@ -139,9 +140,18 @@ export function updateExplore(datasource_type, datasource_id, form_data) {
dispatch(updateChart(JSON.parse(data)));
},
error(error) {
dispatch(chartUpdateFailed(error));
dispatch(chartUpdateFailed(error.responseJSON.error));
},
});
};
}
export const REMOVE_CONTROL_PANEL_ALERT = 'REMOVE_CONTROL_PANEL_ALERT';
export function removeControlPanelAlert() {
return { type: REMOVE_CONTROL_PANEL_ALERT };
}
export const REMOVE_CHART_ALERT = 'REMOVE_CHART_ALERT';
export function removeChartAlert() {
return { type: REMOVE_CHART_ALERT };
}

View File

@ -1,7 +1,7 @@
import $ from 'jquery';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { Panel } from 'react-bootstrap';
import { Panel, Alert } from 'react-bootstrap';
import visMap from '../../../visualizations/main';
import { d3format } from '../../modules/utils';
import ExploreActionButtons from '../../explore/components/ExploreActionButtons';
@ -24,6 +24,7 @@ const propTypes = {
data: PropTypes.any,
isChartLoading: PropTypes.bool,
isStarred: PropTypes.bool.isRequired,
alert: PropTypes.string,
};
class ChartContainer extends React.Component {
@ -139,6 +140,9 @@ class ChartContainer extends React.Component {
};
}
removeAlert() {
this.props.actions.removeChartAlert();
}
renderVis() {
visMap[this.props.viz_type](this.state.mockSlice).render();
@ -183,6 +187,16 @@ class ChartContainer extends React.Component {
</div>
}
>
{this.props.alert &&
<Alert bsStyle="warning">
{this.props.alert}
<i
className="fa fa-close pull-right"
onClick={this.removeAlert.bind(this)}
style={{ cursor: 'pointer' }}
/>
</Alert>
}
{!this.props.isChartLoading &&
<div
id={this.props.containerId}
@ -213,6 +227,7 @@ function mapStateToProps(state) {
data: state.viz.data,
isChartLoading: state.isChartLoading,
isStarred: state.isStarred,
alert: state.chartAlert,
};
}

View File

@ -3,7 +3,7 @@ import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/exploreActions';
import { connect } from 'react-redux';
import { Panel } from 'react-bootstrap';
import { Panel, Alert } from 'react-bootstrap';
import { visTypes, commonControlPanelSections } from '../stores/store';
import ControlPanelSection from './ControlPanelSection';
import FieldSetRow from './FieldSetRow';
@ -15,6 +15,7 @@ const propTypes = {
isDatasourceMetaLoading: PropTypes.bool.isRequired,
form_data: PropTypes.object.isRequired,
y_axis_zero: PropTypes.any,
alert: PropTypes.string,
};
class ControlPanelsContainer extends React.Component {
@ -53,10 +54,23 @@ class ControlPanelsContainer extends React.Component {
const viz = visTypes[this.props.form_data.viz_type];
return viz.fieldOverrides;
}
removeAlert() {
this.props.actions.removeControlPanelAlert();
}
render() {
return (
<Panel>
{this.props.alert &&
<Alert bsStyle="warning">
{this.props.alert}
<i
className="fa fa-close pull-right"
onClick={this.removeAlert.bind(this)}
style={{ cursor: 'pointer' }}
/>
</Alert>
}
{!this.props.isDatasourceMetaLoading &&
<div className="scrollbar-container">
<div className="scrollbar-content">
@ -91,6 +105,7 @@ ControlPanelsContainer.propTypes = propTypes;
function mapStateToProps(state) {
return {
alert: state.controlPanelAlert,
isDatasourceMetaLoading: state.isDatasourceMetaLoading,
fields: state.fields,
datasource_type: state.datasource_type,

View File

@ -41,6 +41,9 @@ class ExploreViewContainer extends React.Component {
const params = $.param(data, true);
this.updateUrl(params);
// remove alerts when query
this.props.actions.removeControlPanelAlert();
this.props.actions.removeChartAlert();
}
getHeight() {
@ -85,6 +88,7 @@ class ExploreViewContainer extends React.Component {
<ChartContainer
actions={this.props.actions}
height={this.state.height}
actions={this.props.actions}
/>
</div>
</div>

View File

@ -18,9 +18,15 @@ export const exploreReducer = function (state, action) {
[actions.FETCH_FAILED]() {
// todo(alanna) handle failure/error state
return Object.assign({}, state, { isDatasourceMetaLoading: false });
return Object.assign({}, state,
{
isDatasourceMetaLoading: false,
controlPanelAlert: action.error,
});
},
[actions.REMOVE_CONTROL_PANEL_ALERT]() {
return Object.assign({}, state, { controlPanelAlert: null });
},
[actions.SET_FIELD_OPTIONS]() {
const newState = Object.assign({}, state);
const optionsByFieldName = action.options;
@ -88,7 +94,10 @@ export const exploreReducer = function (state, action) {
return Object.assign({}, state, { isChartLoading: true });
},
[actions.CHART_UPDATE_FAILED]() {
return Object.assign({}, state, { isChartLoading: false });
return Object.assign({}, state, { isChartLoading: false, chartAlert: action.error });
},
[actions.REMOVE_CHART_ALERT]() {
return Object.assign({}, state, { chartAlert: null });
},
};
if (action.type in actionHandlers) {

View File

@ -1294,16 +1294,20 @@ class Superset(BaseSupersetView):
def update_explore(self, datasource_type, datasource_id):
"""Send back new viz on POST request for updating update explore view"""
form_data = json.loads(request.form.get('data'))
error_redirect = '/slicemodelview/list/'
try:
viz_obj = self.get_viz(
datasource_type=datasource_type,
datasource_id=datasource_id,
args=form_data)
except Exception as e:
flash('{}'.format(e), "alert")
return redirect(error_redirect)
return viz_obj.get_json()
logging.exception(e)
return json_error_response('{}'.format(e))
try:
viz_json = viz_obj.get_json()
except Exception as e:
logging.exception(e)
return json_error_response(utils.error_msg_from_exception(e))
return viz_json
@has_access_api
@expose("/explore_json/<datasource_type>/<datasource_id>/")