/** @format */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { format as formatDate } from '@wordpress/date';
import { map, find, orderBy } from 'lodash';
import PropTypes from 'prop-types';
import { withSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import {
Card,
Chart,
ReportFilters,
SummaryList,
SummaryNumber,
TableCard,
} from '@woocommerce/components';
import { downloadCSVFile, generateCSVDataFromTable, generateCSVFileName } from 'lib/csv';
import { formatCurrency, getCurrencyFormatDecimal } from 'lib/currency';
import { getAdminLink, getNewPath, updateQueryString } from 'lib/nav-utils';
import { getAllReportData, isReportDataEmpty } from 'store/reports/utils';
import { getCurrentDates, isoDateFormat, getPreviousDate, getDateDifferenceInDays } from 'lib/date';
import { MAX_PER_PAGE } from 'store/constants';
export class RevenueReport extends Component {
constructor() {
super();
this.onDownload = this.onDownload.bind( this );
this.onQueryChange = this.onQueryChange.bind( this );
}
onDownload( headers, rows, query ) {
// @TODO The current implementation only downloads the contents displayed in the table.
// Another solution is required when the data set is larger (see #311).
return () => {
downloadCSVFile(
generateCSVFileName( 'revenue', query ),
generateCSVDataFromTable( headers, rows )
);
};
}
/**
* This function returns an event handler for the given `param`
* @todo Move handling of this to a library?
* @param {string} param The parameter in the querystring which should be updated (ex `page`, `per_page`)
* @return {function} A callback which will update `param` to the passed value when called.
*/
onQueryChange( param ) {
switch ( param ) {
case 'sort':
return ( key, dir ) => updateQueryString( { orderby: key, order: dir } );
default:
return value => updateQueryString( { [ param ]: value } );
}
}
getHeadersContent() {
return [
{
label: __( 'Date', 'wc-admin' ),
key: 'date_start',
required: true,
defaultSort: true,
isSortable: true,
},
{
label: __( 'Orders', 'wc-admin' ),
key: 'orders_count',
required: false,
isSortable: true,
},
{
label: __( 'Gross Revenue', 'wc-admin' ),
key: 'gross_revenue',
required: true,
isSortable: true,
isNumeric: true,
},
{
label: __( 'Refunds', 'wc-admin' ),
key: 'refunds',
required: false,
isSortable: true,
isNumeric: true,
},
{
label: __( 'Coupons', 'wc-admin' ),
key: 'coupons',
required: false,
isSortable: true,
isNumeric: true,
},
{
label: __( 'Taxes', 'wc-admin' ),
key: 'taxes',
required: false,
isSortable: true,
isNumeric: true,
},
{
label: __( 'Shipping', 'wc-admin' ),
key: 'shipping',
required: false,
isSortable: true,
isNumeric: true,
},
{
label: __( 'Net Revenue', 'wc-admin' ),
key: 'net_revenue',
required: false,
isSortable: true,
isNumeric: true,
},
];
}
getRowsContent( data = [] ) {
return map( data, row => {
const {
coupons,
gross_revenue,
net_revenue,
orders_count,
refunds,
shipping,
taxes,
} = row.subtotals;
// @TODO How to create this per-report? Can use `w`, `year`, `m` to build time-specific order links
// we need to know which kind of report this is, and parse the `label` to get this row's date
const orderLink = (
{ orders_count }
);
return [
{
display: formatDate( 'm/d/Y', row.date_start ),
value: row.date_start,
},
{
display: orderLink,
value: Number( orders_count ),
},
{
display: formatCurrency( gross_revenue ),
value: getCurrencyFormatDecimal( gross_revenue ),
},
{
display: formatCurrency( refunds ),
value: getCurrencyFormatDecimal( refunds ),
},
{
display: formatCurrency( coupons ),
value: getCurrencyFormatDecimal( coupons ),
},
{
display: formatCurrency( taxes ),
value: getCurrencyFormatDecimal( taxes ),
},
{
display: formatCurrency( shipping ),
value: getCurrencyFormatDecimal( shipping ),
},
{
display: formatCurrency( net_revenue ),
value: getCurrencyFormatDecimal( net_revenue ),
},
];
} );
}
getCharts() {
return [
{
key: 'gross_revenue',
label: __( 'Gross Revenue', 'wc-admin' ),
type: 'currency',
},
{
key: 'refunds',
label: __( 'Refunds', 'wc-admin' ),
type: 'currency',
},
{
key: 'coupons',
label: __( 'Coupons', 'wc-admin' ),
type: 'currency',
},
{
key: 'taxes',
label: __( 'Taxes', 'wc-admin' ),
type: 'currency',
},
{
key: 'shipping',
label: __( 'Shipping', 'wc-admin' ),
type: 'currency',
},
{
key: 'net_revenue',
label: __( 'Net Revenue', 'wc-admin' ),
type: 'currency',
},
];
}
getSelectedChart() {
const { query } = this.props;
const charts = this.getCharts();
const chart = find( charts, { key: query.chart } );
if ( chart ) {
return chart;
}
return charts[ 0 ];
}
// TODO since this pattern will exist on every report, this possibly should become a component
renderChartSummaryNumbers() {
const selectedChart = this.getSelectedChart();
const { primaryData, secondaryData } = this.props;
const { totals } = primaryData.data;
const secondaryTotals = secondaryData.data.totals || {};
const summaryNumbers = map( this.getCharts(), chart => {
const { key, label, type } = chart;
const isSelected = selectedChart.key === key;
let value = parseFloat( totals[ key ] );
let secondaryValue =
( secondaryTotals[ key ] && parseFloat( secondaryTotals[ key ] ) ) || undefined;
let delta = 0;
if ( secondaryValue && secondaryValue !== 0 ) {
delta = Math.round( ( value - secondaryValue ) / secondaryValue * 100 );
}
switch ( type ) {
// TODO: implement other format handlers
case 'currency':
value = formatCurrency( value );
secondaryValue = secondaryValue && formatCurrency( secondaryValue );
break;
}
const href = getNewPath( { chart: key } );
return (
{ message }