2018-10-16 16:07:43 +00:00
|
|
|
/** @format */
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2019-02-12 21:42:02 +00:00
|
|
|
import { __ } from '@wordpress/i18n';
|
2018-11-21 01:19:33 +00:00
|
|
|
import { Component } from '@wordpress/element';
|
2018-10-16 16:07:43 +00:00
|
|
|
import { compose } from '@wordpress/compose';
|
|
|
|
import { format as formatDate } from '@wordpress/date';
|
2019-03-13 10:38:43 +00:00
|
|
|
import { get, isEqual } from 'lodash';
|
2018-10-16 16:07:43 +00:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
|
|
|
/**
|
2018-10-30 18:57:48 +00:00
|
|
|
* WooCommerce dependencies
|
2018-10-16 16:07:43 +00:00
|
|
|
*/
|
|
|
|
import {
|
|
|
|
getAllowedIntervalsForQuery,
|
|
|
|
getCurrentDates,
|
|
|
|
getDateFormatsForInterval,
|
|
|
|
getIntervalForQuery,
|
2018-11-14 01:45:05 +00:00
|
|
|
getChartTypeForQuery,
|
2018-10-16 16:07:43 +00:00
|
|
|
getPreviousDate,
|
2018-10-30 18:57:48 +00:00
|
|
|
} from '@woocommerce/date';
|
2018-11-27 18:30:54 +00:00
|
|
|
import { Chart } from '@woocommerce/components';
|
2018-10-30 18:57:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
2019-01-30 07:22:10 +00:00
|
|
|
import { getReportChartData, getTooltipValueFormat } from 'wc-api/reports/utils';
|
2018-10-17 18:56:50 +00:00
|
|
|
import ReportError from 'analytics/components/report-error';
|
2018-12-04 15:33:30 +00:00
|
|
|
import withSelect from 'wc-api/with-select';
|
2019-03-06 20:12:28 +00:00
|
|
|
import { getChartMode, getSelectedFilter } from './utils';
|
2018-11-20 22:21:47 +00:00
|
|
|
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Component that renders the chart in reports.
|
|
|
|
*/
|
2018-11-20 22:21:47 +00:00
|
|
|
export class ReportChart extends Component {
|
2019-03-13 10:38:43 +00:00
|
|
|
shouldComponentUpdate( nextProps ) {
|
|
|
|
if (
|
|
|
|
nextProps.isRequesting !== this.props.isRequesting ||
|
|
|
|
nextProps.primaryData.isRequesting !== this.props.primaryData.isRequesting ||
|
|
|
|
nextProps.secondaryData.isRequesting !== this.props.secondaryData.isRequesting ||
|
|
|
|
! isEqual( nextProps.query, this.props.query )
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-05 18:12:58 +00:00
|
|
|
getItemChartData() {
|
|
|
|
const { primaryData, selectedChart } = this.props;
|
|
|
|
const chartData = primaryData.data.intervals.map( function( interval ) {
|
|
|
|
const intervalData = {};
|
|
|
|
interval.subtotals.segments.forEach( function( segment ) {
|
|
|
|
if ( segment.segment_label ) {
|
|
|
|
const label = intervalData[ segment.segment_label ]
|
|
|
|
? segment.segment_label + ' (#' + segment.segment_id + ')'
|
|
|
|
: segment.segment_label;
|
2019-03-05 18:27:20 +00:00
|
|
|
intervalData[ segment.segment_id ] = {
|
|
|
|
label,
|
2019-02-05 18:12:58 +00:00
|
|
|
value: segment.subtotals[ selectedChart.key ] || 0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
return {
|
|
|
|
date: formatDate( 'Y-m-d\\TH:i:s', interval.date_start ),
|
|
|
|
...intervalData,
|
|
|
|
};
|
|
|
|
} );
|
|
|
|
return chartData;
|
2018-11-20 22:21:47 +00:00
|
|
|
}
|
|
|
|
|
2019-02-05 18:12:58 +00:00
|
|
|
getTimeChartData() {
|
|
|
|
const { query, primaryData, secondaryData, selectedChart } = this.props;
|
2018-10-16 16:07:43 +00:00
|
|
|
const currentInterval = getIntervalForQuery( query );
|
|
|
|
const { primary, secondary } = getCurrentDates( query );
|
|
|
|
|
|
|
|
const chartData = primaryData.data.intervals.map( function( interval, index ) {
|
|
|
|
const secondaryDate = getPreviousDate(
|
2018-12-27 18:51:58 +00:00
|
|
|
interval.date_start,
|
2018-10-16 16:07:43 +00:00
|
|
|
primary.after,
|
|
|
|
secondary.after,
|
|
|
|
query.compare,
|
|
|
|
currentInterval
|
|
|
|
);
|
|
|
|
|
|
|
|
const secondaryInterval = secondaryData.data.intervals[ index ];
|
|
|
|
return {
|
|
|
|
date: formatDate( 'Y-m-d\\TH:i:s', interval.date_start ),
|
2019-03-05 18:27:20 +00:00
|
|
|
primary: {
|
|
|
|
label: `${ primary.label } (${ primary.range })`,
|
2018-10-16 16:07:43 +00:00
|
|
|
labelDate: interval.date_start,
|
|
|
|
value: interval.subtotals[ selectedChart.key ] || 0,
|
|
|
|
},
|
2019-03-05 18:27:20 +00:00
|
|
|
secondary: {
|
|
|
|
label: `${ secondary.label } (${ secondary.range })`,
|
2018-11-12 14:34:51 +00:00
|
|
|
labelDate: secondaryDate.format( 'YYYY-MM-DD HH:mm:ss' ),
|
2018-10-16 16:07:43 +00:00
|
|
|
value: ( secondaryInterval && secondaryInterval.subtotals[ selectedChart.key ] ) || 0,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} );
|
2018-11-01 09:13:45 +00:00
|
|
|
|
2019-02-05 18:12:58 +00:00
|
|
|
return chartData;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderChart( mode, isRequesting, chartData ) {
|
|
|
|
const {
|
2019-02-12 21:42:02 +00:00
|
|
|
emptySearchResults,
|
2019-03-06 20:12:28 +00:00
|
|
|
filterParam,
|
2019-02-05 18:12:58 +00:00
|
|
|
interactiveLegend,
|
|
|
|
itemsLabel,
|
|
|
|
legendPosition,
|
|
|
|
path,
|
|
|
|
query,
|
|
|
|
selectedChart,
|
|
|
|
showHeaderControls,
|
|
|
|
primaryData,
|
|
|
|
} = this.props;
|
|
|
|
const currentInterval = getIntervalForQuery( query );
|
|
|
|
const allowedIntervals = getAllowedIntervalsForQuery( query );
|
|
|
|
const formats = getDateFormatsForInterval( currentInterval, primaryData.data.intervals.length );
|
2019-02-12 21:42:02 +00:00
|
|
|
const emptyMessage = emptySearchResults
|
2019-03-13 17:14:02 +00:00
|
|
|
? __( 'No data for the current search', 'woocommerce-admin' )
|
|
|
|
: __( 'No data for the selected date range', 'woocommerce-admin' );
|
2018-10-16 16:07:43 +00:00
|
|
|
return (
|
|
|
|
<Chart
|
2019-01-08 09:21:47 +00:00
|
|
|
allowedIntervals={ allowedIntervals }
|
2018-10-16 16:07:43 +00:00
|
|
|
data={ chartData }
|
2019-01-08 09:21:47 +00:00
|
|
|
dateParser={ '%Y-%m-%dT%H:%M:%S' }
|
2019-02-12 21:42:02 +00:00
|
|
|
emptyMessage={ emptyMessage }
|
2019-03-06 20:12:28 +00:00
|
|
|
filterParam={ filterParam }
|
2019-01-08 09:21:47 +00:00
|
|
|
interactiveLegend={ interactiveLegend }
|
2018-10-16 16:07:43 +00:00
|
|
|
interval={ currentInterval }
|
2019-02-05 18:12:58 +00:00
|
|
|
isRequesting={ isRequesting }
|
2018-11-12 21:41:33 +00:00
|
|
|
itemsLabel={ itemsLabel }
|
2019-01-08 09:21:47 +00:00
|
|
|
legendPosition={ legendPosition }
|
2019-02-05 18:12:58 +00:00
|
|
|
mode={ mode }
|
2019-01-08 09:21:47 +00:00
|
|
|
path={ path }
|
|
|
|
query={ query }
|
2019-02-26 10:56:49 +00:00
|
|
|
screenReaderFormat={ formats.screenReaderFormat }
|
2019-01-08 09:21:47 +00:00
|
|
|
showHeaderControls={ showHeaderControls }
|
|
|
|
title={ selectedChart.label }
|
2018-11-12 14:34:51 +00:00
|
|
|
tooltipLabelFormat={ formats.tooltipLabelFormat }
|
2019-02-05 18:12:58 +00:00
|
|
|
tooltipTitle={ ( 'time-comparison' === mode && selectedChart.label ) || null }
|
2019-01-08 09:21:47 +00:00
|
|
|
tooltipValueFormat={ getTooltipValueFormat( selectedChart.type ) }
|
2019-02-14 17:35:08 +00:00
|
|
|
chartType={ getChartTypeForQuery( query ) }
|
2019-01-08 09:21:47 +00:00
|
|
|
valueType={ selectedChart.type }
|
2018-10-16 16:07:43 +00:00
|
|
|
xFormat={ formats.xFormat }
|
|
|
|
x2Format={ formats.x2Format }
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
2019-02-05 18:12:58 +00:00
|
|
|
|
|
|
|
renderItemComparison() {
|
2019-02-27 14:43:34 +00:00
|
|
|
const { isRequesting, primaryData } = this.props;
|
2019-02-05 18:12:58 +00:00
|
|
|
|
|
|
|
if ( primaryData.isError ) {
|
|
|
|
return <ReportError isError />;
|
|
|
|
}
|
|
|
|
|
2019-02-27 14:43:34 +00:00
|
|
|
const isChartRequesting = isRequesting || primaryData.isRequesting;
|
2019-02-05 18:12:58 +00:00
|
|
|
const chartData = this.getItemChartData();
|
|
|
|
|
2019-02-27 14:43:34 +00:00
|
|
|
return this.renderChart( 'item-comparison', isChartRequesting, chartData );
|
2019-02-05 18:12:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
renderTimeComparison() {
|
2019-02-27 14:43:34 +00:00
|
|
|
const { isRequesting, primaryData, secondaryData } = this.props;
|
2019-02-05 18:12:58 +00:00
|
|
|
|
|
|
|
if ( ! primaryData || primaryData.isError || secondaryData.isError ) {
|
|
|
|
return <ReportError isError />;
|
|
|
|
}
|
|
|
|
|
2019-02-27 14:43:34 +00:00
|
|
|
const isChartRequesting =
|
|
|
|
isRequesting || primaryData.isRequesting || secondaryData.isRequesting;
|
2019-02-05 18:12:58 +00:00
|
|
|
const chartData = this.getTimeChartData();
|
|
|
|
|
2019-02-27 14:43:34 +00:00
|
|
|
return this.renderChart( 'time-comparison', isChartRequesting, chartData );
|
2019-02-05 18:12:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { mode } = this.props;
|
|
|
|
if ( 'item-comparison' === mode ) {
|
|
|
|
return this.renderItemComparison();
|
|
|
|
}
|
|
|
|
return this.renderTimeComparison();
|
|
|
|
}
|
2018-10-16 16:07:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ReportChart.propTypes = {
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Filters available for that report.
|
|
|
|
*/
|
2018-11-20 22:21:47 +00:00
|
|
|
filters: PropTypes.array,
|
2019-02-27 14:43:34 +00:00
|
|
|
/**
|
|
|
|
* Whether there is an API call running.
|
|
|
|
*/
|
|
|
|
isRequesting: PropTypes.bool,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Label describing the legend items.
|
|
|
|
*/
|
2018-11-12 21:41:33 +00:00
|
|
|
itemsLabel: PropTypes.string,
|
2019-02-27 16:28:18 +00:00
|
|
|
/**
|
2019-03-07 02:57:22 +00:00
|
|
|
* Allows specifying properties different from the `endpoint` that will be used
|
2019-02-27 16:28:18 +00:00
|
|
|
* to limit the items when there is an active search.
|
|
|
|
*/
|
2019-03-07 02:57:22 +00:00
|
|
|
limitProperties: PropTypes.array,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* `items-comparison` (default) or `time-comparison`, this is used to generate correct
|
|
|
|
* ARIA properties.
|
|
|
|
*/
|
2018-12-22 00:24:26 +00:00
|
|
|
mode: PropTypes.string,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Current path
|
|
|
|
*/
|
2018-11-01 09:13:45 +00:00
|
|
|
path: PropTypes.string.isRequired,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Primary data to display in the chart.
|
|
|
|
*/
|
2019-02-07 09:57:47 +00:00
|
|
|
primaryData: PropTypes.object,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* The query string represented in object form.
|
|
|
|
*/
|
2018-11-01 09:13:45 +00:00
|
|
|
query: PropTypes.object.isRequired,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Secondary data to display in the chart.
|
|
|
|
*/
|
2019-02-05 18:12:58 +00:00
|
|
|
secondaryData: PropTypes.object,
|
2018-12-22 11:46:10 +00:00
|
|
|
/**
|
|
|
|
* Properties of the selected chart.
|
|
|
|
*/
|
2019-05-20 01:57:06 +00:00
|
|
|
selectedChart: PropTypes.shape( {
|
|
|
|
/**
|
|
|
|
* Key of the selected chart.
|
|
|
|
*/
|
|
|
|
key: PropTypes.string.isRequired,
|
|
|
|
/**
|
|
|
|
* Chart label.
|
|
|
|
*/
|
|
|
|
label: PropTypes.string.isRequired,
|
|
|
|
/**
|
|
|
|
* Order query argument.
|
|
|
|
*/
|
|
|
|
order: PropTypes.oneOf( [ 'asc', 'desc' ] ),
|
|
|
|
/**
|
|
|
|
* Order by query argument.
|
|
|
|
*/
|
|
|
|
orderby: PropTypes.string,
|
|
|
|
/**
|
|
|
|
* Number type for formatting.
|
|
|
|
*/
|
|
|
|
type: PropTypes.oneOf( [ 'average', 'number', 'currency' ] ).isRequired,
|
|
|
|
} ).isRequired,
|
2018-10-16 16:07:43 +00:00
|
|
|
};
|
|
|
|
|
2019-02-07 09:57:47 +00:00
|
|
|
ReportChart.defaultProps = {
|
2019-02-27 14:43:34 +00:00
|
|
|
isRequesting: false,
|
2019-02-07 09:57:47 +00:00
|
|
|
primaryData: {
|
|
|
|
data: {
|
|
|
|
intervals: [],
|
|
|
|
},
|
|
|
|
isError: false,
|
|
|
|
isRequesting: false,
|
|
|
|
},
|
|
|
|
secondaryData: {
|
|
|
|
data: {
|
|
|
|
intervals: [],
|
|
|
|
},
|
|
|
|
isError: false,
|
|
|
|
isRequesting: false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2018-10-16 16:07:43 +00:00
|
|
|
export default compose(
|
|
|
|
withSelect( ( select, props ) => {
|
2019-03-21 03:25:05 +00:00
|
|
|
const { endpoint, filters, isRequesting, limitProperties, query, advancedFilters } = props;
|
2019-03-07 02:57:22 +00:00
|
|
|
const limitBy = limitProperties || [ endpoint ];
|
2019-03-06 20:12:28 +00:00
|
|
|
const selectedFilter = getSelectedFilter( filters, query );
|
|
|
|
const filterParam = get( selectedFilter, [ 'settings', 'param' ] );
|
|
|
|
const chartMode = props.mode || getChartMode( selectedFilter, query ) || 'time-comparison';
|
|
|
|
|
|
|
|
const newProps = {
|
|
|
|
mode: chartMode,
|
|
|
|
filterParam,
|
|
|
|
};
|
2019-02-05 18:12:58 +00:00
|
|
|
|
2019-02-27 14:43:34 +00:00
|
|
|
if ( isRequesting ) {
|
2019-03-06 20:12:28 +00:00
|
|
|
return newProps;
|
2019-02-27 14:43:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 02:57:22 +00:00
|
|
|
const hasLimitByParam = limitBy.some( item => query[ item ] && query[ item ].length );
|
|
|
|
|
|
|
|
if ( query.search && ! hasLimitByParam ) {
|
2019-02-07 09:57:47 +00:00
|
|
|
return {
|
2019-03-06 20:12:28 +00:00
|
|
|
...newProps,
|
2019-02-12 21:42:02 +00:00
|
|
|
emptySearchResults: true,
|
2019-02-07 09:57:47 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-03-21 03:25:05 +00:00
|
|
|
const primaryData = getReportChartData( {
|
|
|
|
endpoint,
|
|
|
|
dataType: 'primary',
|
|
|
|
query,
|
|
|
|
select,
|
|
|
|
limitBy,
|
|
|
|
filters,
|
|
|
|
advancedFilters,
|
|
|
|
} );
|
|
|
|
|
2019-02-05 18:12:58 +00:00
|
|
|
if ( 'item-comparison' === chartMode ) {
|
|
|
|
return {
|
2019-03-06 20:12:28 +00:00
|
|
|
...newProps,
|
2019-02-05 18:12:58 +00:00
|
|
|
primaryData,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-03-21 03:25:05 +00:00
|
|
|
const secondaryData = getReportChartData( {
|
|
|
|
endpoint,
|
|
|
|
dataType: 'secondary',
|
|
|
|
query,
|
|
|
|
select,
|
|
|
|
limitBy,
|
|
|
|
filters,
|
|
|
|
advancedFilters,
|
|
|
|
} );
|
2018-10-16 16:07:43 +00:00
|
|
|
return {
|
2019-03-06 20:12:28 +00:00
|
|
|
...newProps,
|
2018-10-16 16:07:43 +00:00
|
|
|
primaryData,
|
|
|
|
secondaryData,
|
|
|
|
};
|
|
|
|
} )
|
|
|
|
)( ReportChart );
|