diff --git a/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx b/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx
index a9bdb0f744..7307dd316a 100644
--- a/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx
+++ b/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx
@@ -15,10 +15,10 @@ import {
} from 'react-bootstrap';
import SouthPane from './SouthPane';
-import Timer from './Timer';
-
+import Timer from '../../components/Timer';
import SqlEditorLeftBar from './SqlEditorLeftBar';
import AceEditorWrapper from './AceEditorWrapper';
+import { STATE_BSSTYLE_MAP } from '../constants.js';
const propTypes = {
actions: React.PropTypes.object.isRequired,
@@ -208,7 +208,14 @@ class SqlEditor extends React.PureComponent {
{limitWarning}
-
+ {this.props.latestQuery &&
+
+ }
);
diff --git a/superset/assets/javascripts/SqlLab/components/Timer.jsx b/superset/assets/javascripts/SqlLab/components/Timer.jsx
deleted file mode 100644
index 47c56ce825..0000000000
--- a/superset/assets/javascripts/SqlLab/components/Timer.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import { now, fDuration } from '../../modules/dates';
-
-import { STATE_BSSTYLE_MAP } from '../constants.js';
-
-class Timer extends React.PureComponent {
- constructor(props) {
- super(props);
- this.state = {
- clockStr: '',
- };
- }
- componentWillMount() {
- this.startTimer();
- }
- componentWillUnmount() {
- this.stopTimer();
- }
- startTimer() {
- if (!(this.timer)) {
- this.timer = setInterval(this.stopwatch.bind(this), 30);
- }
- }
- stopTimer() {
- clearInterval(this.timer);
- this.timer = null;
- }
- stopwatch() {
- if (this.props && this.props.query) {
- const endDttm = this.props.query.endDttm || now();
- const clockStr = fDuration(this.props.query.startDttm, endDttm);
- this.setState({ clockStr });
- if (this.props.query.state !== 'running') {
- this.stopTimer();
- }
- }
- }
- render() {
- if (this.props.query && this.props.query.state === 'running') {
- this.startTimer();
- }
- let timerSpan = null;
- if (this.props && this.props.query) {
- const bsStyle = STATE_BSSTYLE_MAP[this.props.query.state];
- timerSpan = (
-
- {this.state.clockStr}
-
- );
- }
- return timerSpan;
- }
-}
-Timer.propTypes = {
- query: React.PropTypes.object,
-};
-Timer.defaultProps = {
- query: null,
-};
-
-export default Timer;
diff --git a/superset/assets/javascripts/components/Timer.jsx b/superset/assets/javascripts/components/Timer.jsx
new file mode 100644
index 0000000000..98b5883a63
--- /dev/null
+++ b/superset/assets/javascripts/components/Timer.jsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { now, fDuration } from '../modules/dates';
+
+class Timer extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ clockStr: '',
+ };
+ }
+ componentWillMount() {
+ this.startTimer();
+ }
+ componentWillUnmount() {
+ this.stopTimer();
+ }
+ startTimer() {
+ if (!(this.timer)) {
+ this.timer = setInterval(this.stopwatch.bind(this), 30);
+ }
+ }
+ stopTimer() {
+ clearInterval(this.timer);
+ this.timer = null;
+ }
+ stopwatch() {
+ if (this.props && this.props.startTime) {
+ const endDttm = this.props.endTime || now();
+ if (this.props.startTime < endDttm) {
+ const clockStr = fDuration(this.props.startTime, endDttm);
+ this.setState({ clockStr });
+ }
+ if (!this.props.isRunning) {
+ this.stopTimer();
+ }
+ }
+ }
+ render() {
+ if (this.props && this.props.isRunning) {
+ this.startTimer();
+ }
+ let timerSpan = null;
+ if (this.props) {
+ timerSpan = (
+
+ {this.state.clockStr}
+
+ );
+ }
+ return timerSpan;
+ }
+}
+Timer.propTypes = {
+ startTime: React.PropTypes.number,
+ endTime: React.PropTypes.number,
+ isRunning: React.PropTypes.bool.isRequired,
+ status: React.PropTypes.string,
+ style: React.PropTypes.object,
+};
+
+Timer.defaultProps = {
+ startTime: null,
+ endTime: null,
+ status: 'success',
+ style: null,
+};
+
+export default Timer;
diff --git a/superset/assets/javascripts/explorev2/actions/exploreActions.js b/superset/assets/javascripts/explorev2/actions/exploreActions.js
index 1d48975e8a..08849e9b2f 100644
--- a/superset/assets/javascripts/explorev2/actions/exploreActions.js
+++ b/superset/assets/javascripts/explorev2/actions/exploreActions.js
@@ -115,7 +115,7 @@ export function chartUpdateFailed(error) {
export function updateExplore(datasource_type, datasource_id, form_data) {
return function (dispatch) {
- dispatch(chartUpdateStarted);
+ dispatch(chartUpdateStarted());
const updateUrl =
`/superset/update_explore/${datasource_type}/${datasource_id}/`;
@@ -194,3 +194,8 @@ export function saveSlice(url) {
});
};
}
+
+export const UPDATE_CHART_STATUS = 'UPDATE_CHART_STATUS';
+export function updateChartStatus(status) {
+ return { type: UPDATE_CHART_STATUS, status };
+}
diff --git a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx
index 52f9692646..7cb2711482 100644
--- a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx
+++ b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx
@@ -7,6 +7,13 @@ import { d3format } from '../../modules/utils';
import ExploreActionButtons from '../../explore/components/ExploreActionButtons';
import FaveStar from '../../components/FaveStar';
import TooltipWrapper from '../../components/TooltipWrapper';
+import Timer from '../../components/Timer';
+
+const CHART_STATUS_MAP = {
+ failed: 'danger',
+ loading: 'warning',
+ success: 'success',
+};
const propTypes = {
actions: PropTypes.object.isRequired,
@@ -22,8 +29,10 @@ const propTypes = {
query: PropTypes.string.isRequired,
column_formats: PropTypes.object,
data: PropTypes.any,
- isChartLoading: PropTypes.bool,
+ chartStatus: PropTypes.bool,
isStarred: PropTypes.bool.isRequired,
+ chartUpdateStartTime: PropTypes.string.isRequired,
+ chartUpdateEndTime: PropTypes.string.isRequired,
alert: PropTypes.string,
table_name: PropTypes.string,
};
@@ -157,7 +166,7 @@ class ChartContainer extends React.Component {
);
}
- if (this.props.isChartLoading) {
+ if (this.props.chartStatus === 'loading') {
return (
);
}
return (
@@ -205,6 +214,13 @@ class ChartContainer extends React.Component {
}
+
{
const mockedProps = {
- query: queries[0],
+ startTime: now(),
+ endTime: null,
+ isRunning: true,
+ state: 'warning',
};
it('renders', () => {
expect(React.isValidElement()).to.equal(true);