2018-06-14 20:15:11 +00:00
|
|
|
/** @format */
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import { __ } from '@wordpress/i18n';
|
|
|
|
import { Component, Fragment } from '@wordpress/element';
|
2018-08-01 16:00:45 +00:00
|
|
|
import { format as formatDate } from '@wordpress/date';
|
|
|
|
import { map, noop } from 'lodash';
|
2018-06-14 20:15:11 +00:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
2018-08-20 21:18:13 +00:00
|
|
|
import {
|
|
|
|
Card,
|
|
|
|
Chart,
|
|
|
|
ReportFilters,
|
|
|
|
SummaryList,
|
|
|
|
SummaryNumber,
|
|
|
|
TableCard,
|
|
|
|
} from '@woocommerce/components';
|
2018-08-01 16:00:45 +00:00
|
|
|
import { formatCurrency, getCurrencyFormatDecimal } from 'lib/currency';
|
2018-08-20 20:40:17 +00:00
|
|
|
import { getAdminLink, getNewPath, updateQueryString } from 'lib/nav-utils';
|
2018-06-26 21:02:08 +00:00
|
|
|
import { getReportData } from 'lib/swagger';
|
2018-08-01 16:00:45 +00:00
|
|
|
|
|
|
|
// Mock data until we fetch from an API
|
|
|
|
import rawData from './mock-data';
|
2018-08-17 22:47:54 +00:00
|
|
|
import testData from './data';
|
|
|
|
const charts = {
|
|
|
|
gross_revenue: {
|
|
|
|
label: __( 'Gross Revenue', 'wc-admin' ),
|
|
|
|
type: 'currency',
|
|
|
|
},
|
|
|
|
refunds: {
|
|
|
|
label: __( 'Refunds', 'wc-admin' ),
|
|
|
|
type: 'currency',
|
|
|
|
},
|
|
|
|
coupons: {
|
|
|
|
label: __( 'Coupons', 'wc-admin' ),
|
|
|
|
type: 'currency',
|
|
|
|
},
|
|
|
|
taxes: {
|
|
|
|
label: __( 'Taxes', 'wc-admin' ),
|
|
|
|
type: 'currency',
|
|
|
|
},
|
|
|
|
};
|
2018-06-20 15:09:37 +00:00
|
|
|
|
2018-06-14 20:15:11 +00:00
|
|
|
class RevenueReport extends Component {
|
2018-06-26 14:59:35 +00:00
|
|
|
constructor() {
|
|
|
|
super();
|
2018-08-01 16:00:45 +00:00
|
|
|
this.onQueryChange = this.onQueryChange.bind( this );
|
2018-06-26 21:02:08 +00:00
|
|
|
|
|
|
|
// TODO remove this when we implement real endpoints
|
|
|
|
this.state = { stats: {} };
|
2018-06-26 14:59:35 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 21:02:08 +00:00
|
|
|
componentDidMount() {
|
|
|
|
// Swagger doesn't support returning different data based on query args
|
|
|
|
// this is more or less to show how we will manipulate data calls based on props
|
|
|
|
const statsQueryArgs = {
|
|
|
|
interval: 'week',
|
|
|
|
after: '2018-04-22',
|
|
|
|
before: '2018-05-06',
|
|
|
|
};
|
|
|
|
|
|
|
|
getReportData( 'revenue/stats', statsQueryArgs ).then( response => {
|
|
|
|
if ( ! response.ok ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-01 16:00:45 +00:00
|
|
|
response.json().then( () => {
|
|
|
|
// Ignore data, just use our fake data once we have a response
|
|
|
|
this.setState( { stats: rawData } );
|
2018-06-26 21:02:08 +00:00
|
|
|
} );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2018-08-01 16:00:45 +00:00
|
|
|
/**
|
|
|
|
* This function returns an event handler for the given `param`
|
2018-08-06 17:01:41 +00:00
|
|
|
* @todo Move handling of this to a library?
|
2018-08-01 16:00:45 +00:00
|
|
|
* @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 ) {
|
2018-08-06 17:01:41 +00:00
|
|
|
switch ( param ) {
|
|
|
|
case 'sort':
|
|
|
|
return ( key, dir ) => updateQueryString( { orderby: key, order: dir } );
|
|
|
|
default:
|
|
|
|
return value => updateQueryString( { [ param ]: value } );
|
|
|
|
}
|
2018-08-01 16:00:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getHeadersContent() {
|
|
|
|
return [
|
2018-08-06 17:01:41 +00:00
|
|
|
{
|
|
|
|
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,
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'Refunds', 'wc-admin' ),
|
|
|
|
key: 'refunds',
|
|
|
|
required: false,
|
|
|
|
isSortable: true,
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'Coupons', 'wc-admin' ),
|
|
|
|
key: 'coupons',
|
|
|
|
required: false,
|
|
|
|
isSortable: true,
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'Taxes', 'wc-admin' ),
|
|
|
|
key: 'taxes',
|
|
|
|
required: false,
|
|
|
|
isSortable: false, // For example
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'Shipping', 'wc-admin' ),
|
|
|
|
key: 'shipping',
|
|
|
|
required: false,
|
|
|
|
isSortable: true,
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'Net Revenue', 'wc-admin' ),
|
|
|
|
key: 'net_revenue',
|
|
|
|
required: false,
|
|
|
|
isSortable: true,
|
2018-08-13 15:25:32 +00:00
|
|
|
isNumeric: true,
|
2018-08-06 17:01:41 +00:00
|
|
|
},
|
2018-08-01 16:00:45 +00:00
|
|
|
];
|
2018-06-26 14:59:35 +00:00
|
|
|
}
|
|
|
|
|
2018-08-01 16:00:45 +00:00
|
|
|
getRowsContent( data = [] ) {
|
2018-06-26 22:17:05 +00:00
|
|
|
return map( data, row => {
|
|
|
|
const {
|
|
|
|
coupons,
|
|
|
|
gross_revenue,
|
|
|
|
net_revenue,
|
|
|
|
orders_count,
|
|
|
|
refunds,
|
|
|
|
shipping,
|
|
|
|
taxes,
|
|
|
|
} = row.subtotals;
|
|
|
|
|
2018-07-13 19:36:41 +00:00
|
|
|
// @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
|
2018-06-20 15:09:37 +00:00
|
|
|
const orderLink = (
|
|
|
|
<a href={ getAdminLink( '/edit.php?post_type=shop_order&w=4&year=2018' ) }>
|
2018-06-26 22:17:05 +00:00
|
|
|
{ orders_count }
|
2018-06-20 15:09:37 +00:00
|
|
|
</a>
|
|
|
|
);
|
|
|
|
return [
|
2018-08-01 16:00:45 +00:00
|
|
|
{
|
|
|
|
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 ),
|
|
|
|
},
|
2018-06-20 15:09:37 +00:00
|
|
|
];
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2018-08-01 16:00:45 +00:00
|
|
|
getSummaryContent( data = {} ) {
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
label: __( 'gross revenue', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.gross_revenue ),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'refunds', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.refunds ),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'coupons', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.coupons ),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'taxes', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.taxes ),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'shipping', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.shipping ),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __( 'net revenue', 'wc-admin' ),
|
|
|
|
value: formatCurrency( data.net_revenue ),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2018-08-17 22:47:54 +00:00
|
|
|
// TODO since this pattern will exist on every report, this possibly should become a component
|
|
|
|
getChartSummaryNumbers() {
|
|
|
|
const { totals = {} } = this.state.stats;
|
|
|
|
const selectedChart = this.getSelectedChart();
|
|
|
|
|
|
|
|
const summaryNumbers = map( charts, ( chart, key ) => {
|
|
|
|
const { label, type } = chart;
|
|
|
|
const isSelected = selectedChart === key;
|
|
|
|
let value = totals[ key ];
|
|
|
|
|
|
|
|
switch ( type ) {
|
|
|
|
// TODO: implement other format handlers
|
|
|
|
case 'currency':
|
|
|
|
value = formatCurrency( value );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-20 20:40:17 +00:00
|
|
|
const href = getNewPath( { chart: key } );
|
2018-08-17 22:47:54 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<SummaryNumber
|
|
|
|
key={ key }
|
|
|
|
value={ value }
|
|
|
|
label={ label }
|
|
|
|
selected={ isSelected }
|
|
|
|
delta={ 0 }
|
2018-08-20 20:40:17 +00:00
|
|
|
href={ href }
|
2018-08-17 22:47:54 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
|
|
|
return <SummaryList>{ summaryNumbers }</SummaryList>;
|
|
|
|
}
|
|
|
|
|
|
|
|
getSelectedChart() {
|
|
|
|
const { query } = this.props;
|
|
|
|
const { chart } = query;
|
|
|
|
if ( chart && charts[ chart ] ) {
|
|
|
|
return chart;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'gross_revenue';
|
|
|
|
}
|
|
|
|
|
2018-06-14 20:15:11 +00:00
|
|
|
render() {
|
|
|
|
const { path, query } = this.props;
|
2018-08-01 16:00:45 +00:00
|
|
|
const { totals = {}, intervals = [] } = this.state.stats;
|
|
|
|
const summary = this.getSummaryContent( totals ) || [];
|
|
|
|
const rows = this.getRowsContent( intervals ) || [];
|
|
|
|
const headers = this.getHeadersContent();
|
2018-08-17 22:47:54 +00:00
|
|
|
const selectedChart = this.getSelectedChart();
|
2018-06-20 15:09:37 +00:00
|
|
|
|
2018-06-14 20:15:11 +00:00
|
|
|
return (
|
|
|
|
<Fragment>
|
2018-08-08 21:24:48 +00:00
|
|
|
<ReportFilters query={ query } path={ path } />
|
2018-06-14 20:15:11 +00:00
|
|
|
|
2018-08-17 22:47:54 +00:00
|
|
|
{ this.getChartSummaryNumbers() }
|
|
|
|
<Card title="">
|
|
|
|
<Chart data={ testData[ selectedChart ] } title={ charts[ selectedChart ].label } />
|
|
|
|
</Card>
|
2018-06-26 14:59:35 +00:00
|
|
|
|
2018-08-01 16:00:45 +00:00
|
|
|
<TableCard
|
|
|
|
title={ __( 'Revenue Last Week', 'wc-admin' ) }
|
|
|
|
rows={ rows }
|
|
|
|
headers={ headers }
|
|
|
|
onClickDownload={ noop }
|
|
|
|
onQueryChange={ this.onQueryChange }
|
|
|
|
query={ query }
|
|
|
|
summary={ summary }
|
2018-06-26 14:59:35 +00:00
|
|
|
/>
|
2018-06-14 20:15:11 +00:00
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RevenueReport.propTypes = {
|
|
|
|
params: PropTypes.object.isRequired,
|
|
|
|
path: PropTypes.string.isRequired,
|
|
|
|
query: PropTypes.object.isRequired,
|
|
|
|
};
|
|
|
|
|
|
|
|
export default RevenueReport;
|