woocommerce/plugins/woocommerce-admin/client/analytics/components/report-chart/utils.js

192 lines
5.4 KiB
JavaScript
Raw Normal View History

/**
* External dependencies
*/
import { find, get } from 'lodash';
import { flattenFilters } from '@woocommerce/navigation';
import { format as formatDate } from '@wordpress/date';
import {
containsLeapYear,
getPreviousDate,
isLeapYear,
} from '@woocommerce/date';
export const DEFAULT_FILTER = 'all';
export function getSelectedFilter( filters, query, selectedFilterArgs = {} ) {
if ( ! filters || filters.length === 0 ) {
return null;
}
const clonedFilters = filters.slice( 0 );
const filterConfig = clonedFilters.pop();
if ( filterConfig.showFilters( query, selectedFilterArgs ) ) {
const allFilters = flattenFilters( filterConfig.filters );
const value =
query[ filterConfig.param ] ||
filterConfig.defaultValue ||
DEFAULT_FILTER;
return find( allFilters, { value } );
}
return getSelectedFilter( clonedFilters, query, selectedFilterArgs );
}
export function getChartMode( selectedFilter, query ) {
if ( selectedFilter && query ) {
const selectedFilterParam = get( selectedFilter, [
'settings',
'param',
] );
if (
! selectedFilterParam ||
Object.keys( query ).includes( selectedFilterParam )
) {
return get( selectedFilter, [ 'chartMode' ] );
}
}
return null;
}
export function createDateFormatter( format ) {
return ( date ) => formatDate( format, date );
}
/**
* Returns true if the data contains a leap year.
*
* @param {Object} data Chart interval data
* @return {boolean} True if data contains a leap year.
*/
export function dataContainsLeapYear( data ) {
if ( data?.data?.intervals?.length > 1 ) {
const start = data.data.intervals[ 0 ].date_start;
const end =
data.data.intervals[ data.data.intervals.length - 1 ].date_end;
if ( containsLeapYear( start, end ) ) {
return true;
}
}
return false;
}
/**
* Builds chart data for the given parameters.
*
* @param {Object} primaryData Primary data
* @param {Object} secondaryData Secondary data
* @param {Object} primaryDatePicker DataPickerOptions object for primary data
* @param {Object} secondaryDatePicker DataPickerOptions object for secondary data
* @param {string} comparison Comparison type, e.x: `previous_year`
* @param {string} selectedChartKey Chart key, e.x: `orders_count`
* @param {string} currentInterval Chart interval, e.x: `day`
* @return {Object} Chart data
*/
export function buildChartData(
primaryData,
secondaryData,
primaryDatePicker,
secondaryDatePicker,
comparison,
selectedChartKey,
currentInterval
) {
const primarydataContainsLeapYear = dataContainsLeapYear( primaryData );
const secondarydataContainsLeapYear = dataContainsLeapYear( secondaryData );
const primaryDataIntervals = [ ...primaryData.data.intervals ];
const secondaryDataIntervals = [ ...secondaryData.data.intervals ];
const chartData = [];
for ( let index = 0; index < primaryDataIntervals.length; index++ ) {
const interval = primaryDataIntervals[ index ];
const primaryDateFormatted = formatDate(
'Y-m-d\\TH:i:s',
interval.date_start
);
const primaryLabel = `${ primaryDatePicker.label } (${ primaryDatePicker.range })`;
const primaryLabelDate = interval.date_start;
const primaryValue = interval.subtotals[ selectedChartKey ] || 0;
const secondaryInterval = secondaryDataIntervals[ index ];
const secondaryLabel = `${ secondaryDatePicker.label } (${ secondaryDatePicker.range })`;
const secondaryDateMoment = getPreviousDate(
interval.date_start,
primaryDatePicker.after,
secondaryDatePicker.after,
comparison,
currentInterval
);
let secondaryLabelDate = secondaryDateMoment.format(
'YYYY-MM-DD HH:mm:ss'
);
let secondaryValue =
( secondaryInterval &&
secondaryInterval.subtotals[ selectedChartKey ] ) ||
0;
if ( currentInterval === 'day' ) {
if (
primarydataContainsLeapYear &&
! secondarydataContainsLeapYear &&
secondaryDataIntervals?.[ index ]
) {
// Only fix the data if the date is in 29th Feb and secondary data is in 1st March,
// which signifies incorrect comparison.
const primaryDate = new Date( interval.date_start );
const secondaryDate = new Date(
secondaryDataIntervals[ index ].date_start
);
if (
isLeapYear( primaryDate.getFullYear() ) &&
primaryDate.getMonth() === 1 &&
primaryDate.getDate() === 29 &&
secondaryDate.getMonth() === 2 &&
secondaryDate.getDate() === 1
) {
// This is going to be displayed as "Invalid date" label from D3.js, but desirable imo since
// 29th February is not a valid date for non-leap years.
secondaryLabelDate = '-';
secondaryValue = 0;
// Move the data up by 1 day for the missing leap day
// so everything else is shifted to the right correctly.
secondaryDataIntervals.splice(
index,
0,
secondaryDataIntervals[ index ]
);
}
} else if (
! primarydataContainsLeapYear &&
secondarydataContainsLeapYear
) {
// Todo: Do something about secondary data having leap year while first does not.
// Currently, there are issues to render chart where primary data does not have the date since
// the x-axis is based on primary data.
}
}
chartData.push( {
date: primaryDateFormatted,
primary: {
label: primaryLabel,
labelDate: primaryLabelDate,
value: primaryValue,
},
secondary: {
label: secondaryLabel,
labelDate: secondaryLabelDate,
value: secondaryValue,
},
} );
}
return chartData;
}