Add performance indicators to dashboard (https://github.com/woocommerce/woocommerce-admin/pull/1343)
* Add label and generated report URL to the performance indicator response. * Hook up performance indicators to the REST API * Handle PR feedback * Fix setting default values
This commit is contained in:
commit
2f60837ba5
|
@ -19,7 +19,7 @@ import { SummaryList, SummaryListPlaceholder, SummaryNumber } from '@woocommerce
|
||||||
*/
|
*/
|
||||||
import { getSummaryNumbers } from 'store/reports/utils';
|
import { getSummaryNumbers } from 'store/reports/utils';
|
||||||
import ReportError from 'analytics/components/report-error';
|
import ReportError from 'analytics/components/report-error';
|
||||||
import { calculateDelta, formatValue } from './utils';
|
import { calculateDelta, formatValue } from 'lib/number';
|
||||||
import withSelect from 'wc-api/with-select';
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/** @format */
|
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { isFinite } from 'lodash';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WooCommerce dependencies
|
|
||||||
*/
|
|
||||||
import { formatCurrency } from '@woocommerce/currency';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { numberFormat } from 'lib/number';
|
|
||||||
|
|
||||||
export function formatValue( type, value ) {
|
|
||||||
if ( ! isFinite( value ) ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( type ) {
|
|
||||||
case 'average':
|
|
||||||
return Math.round( value );
|
|
||||||
case 'currency':
|
|
||||||
return formatCurrency( value );
|
|
||||||
case 'number':
|
|
||||||
return numberFormat( value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function calculateDelta( primaryValue, secondaryValue ) {
|
|
||||||
if ( ! isFinite( primaryValue ) || ! isFinite( secondaryValue ) ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( secondaryValue === 0 ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round( ( primaryValue - secondaryValue ) / secondaryValue * 100 );
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ export default class Dashboard extends Component {
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Header sections={ [ __( 'Dashboard', 'wc-admin' ) ] } />
|
<Header sections={ [ __( 'Dashboard', 'wc-admin' ) ] } />
|
||||||
<ReportFilters query={ query } path={ path } />
|
<ReportFilters query={ query } path={ path } />
|
||||||
<StorePerformance />
|
<StorePerformance query={ query } />
|
||||||
<Leaderboards query={ query } />
|
<Leaderboards query={ query } />
|
||||||
<DashboardCharts query={ query } path={ path } />
|
<DashboardCharts query={ query } path={ path } />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -2,9 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { ToggleControl } from '@wordpress/components';
|
import { ToggleControl } from '@wordpress/components';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { compose } from '@wordpress/compose';
|
||||||
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { getCurrentDates, appendTimestamp, getDateParamsFromQuery } from '@woocommerce/date';
|
||||||
|
import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -16,99 +26,202 @@ import {
|
||||||
MenuTitle,
|
MenuTitle,
|
||||||
SectionHeader,
|
SectionHeader,
|
||||||
SummaryList,
|
SummaryList,
|
||||||
|
SummaryListPlaceholder,
|
||||||
SummaryNumber,
|
SummaryNumber,
|
||||||
} from '@woocommerce/components';
|
} from '@woocommerce/components';
|
||||||
|
import withSelect from 'wc-api/with-select';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import { calculateDelta, formatValue } from 'lib/number';
|
||||||
|
|
||||||
class StorePerformance extends Component {
|
class StorePerformance extends Component {
|
||||||
constructor() {
|
constructor( props ) {
|
||||||
super( ...arguments );
|
super( props );
|
||||||
this.state = {
|
this.state = {
|
||||||
showCustomers: true,
|
userPrefs: props.userPrefs || [],
|
||||||
showProducts: true,
|
|
||||||
showOrders: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.toggle = this.toggle.bind( this );
|
this.toggle = this.toggle.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle( type ) {
|
toggle( statKey ) {
|
||||||
return () => {
|
return () => {
|
||||||
this.setState( state => ( { [ type ]: ! state[ type ] } ) );
|
this.setState( state => {
|
||||||
|
const prefs = [ ...state.userPrefs ];
|
||||||
|
let newPrefs = [];
|
||||||
|
if ( ! prefs.includes( statKey ) ) {
|
||||||
|
prefs.push( statKey );
|
||||||
|
newPrefs = prefs;
|
||||||
|
} else {
|
||||||
|
newPrefs = prefs.filter( pref => pref !== statKey );
|
||||||
|
}
|
||||||
|
this.props.updateCurrentUserData( {
|
||||||
|
dashboard_performance_indicators: newPrefs,
|
||||||
|
} );
|
||||||
|
return {
|
||||||
|
userPrefs: newPrefs,
|
||||||
|
};
|
||||||
|
} );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMenu() {
|
renderMenu() {
|
||||||
|
const { indicators } = this.props;
|
||||||
return (
|
return (
|
||||||
<EllipsisMenu label={ __( 'Choose which analytics to display', 'wc-admin' ) }>
|
<EllipsisMenu label={ __( 'Choose which analytics to display', 'wc-admin' ) }>
|
||||||
<MenuTitle>{ __( 'Display Stats:', 'wc-admin' ) }</MenuTitle>
|
<MenuTitle>{ __( 'Display Stats:', 'wc-admin' ) }</MenuTitle>
|
||||||
<MenuItem onInvoke={ this.toggle( 'showCustomers' ) }>
|
{ indicators.map( ( indicator, i ) => {
|
||||||
<ToggleControl
|
const checked = ! this.state.userPrefs.includes( indicator.stat );
|
||||||
label={ __( 'Show Customers', 'wc-admin' ) }
|
return (
|
||||||
checked={ this.state.showCustomers }
|
<MenuItem onInvoke={ this.toggle( indicator.stat ) } key={ i }>
|
||||||
onChange={ this.toggle( 'showCustomers' ) }
|
<ToggleControl
|
||||||
/>
|
label={ sprintf( __( 'Show %s', 'wc-admin' ), indicator.label ) }
|
||||||
</MenuItem>
|
checked={ checked }
|
||||||
<MenuItem onInvoke={ this.toggle( 'showProducts' ) }>
|
onChange={ this.toggle( indicator.stat ) }
|
||||||
<ToggleControl
|
/>
|
||||||
label={ __( 'Show Products', 'wc-admin' ) }
|
</MenuItem>
|
||||||
checked={ this.state.showProducts }
|
);
|
||||||
onChange={ this.toggle( 'showProducts' ) }
|
} ) }
|
||||||
/>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onInvoke={ this.toggle( 'showOrders' ) }>
|
|
||||||
<ToggleControl
|
|
||||||
label={ __( 'Show Orders', 'wc-admin' ) }
|
|
||||||
checked={ this.state.showOrders }
|
|
||||||
onChange={ this.toggle( 'showOrders' ) }
|
|
||||||
/>
|
|
||||||
</MenuItem>
|
|
||||||
</EllipsisMenu>
|
</EllipsisMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderList() {
|
||||||
const totalOrders = 10;
|
const {
|
||||||
const totalProducts = 1000;
|
query,
|
||||||
const { showCustomers, showProducts, showOrders } = this.state;
|
primaryRequesting,
|
||||||
|
secondaryRequesting,
|
||||||
|
primaryError,
|
||||||
|
secondaryError,
|
||||||
|
primaryData,
|
||||||
|
secondaryData,
|
||||||
|
userIndicators,
|
||||||
|
} = this.props;
|
||||||
|
if ( primaryRequesting || secondaryRequesting ) {
|
||||||
|
return <SummaryListPlaceholder numberOfItems={ userIndicators.length } />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( primaryError || secondaryError ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const persistedQuery = getPersistedQuery( query );
|
||||||
|
|
||||||
|
const { compare } = getDateParamsFromQuery( query );
|
||||||
|
const prevLabel =
|
||||||
|
'previous_period' === compare
|
||||||
|
? __( 'Previous Period:', 'wc-admin' )
|
||||||
|
: __( 'Previous Year:', 'wc-admin' );
|
||||||
|
return (
|
||||||
|
<SummaryList>
|
||||||
|
{ userIndicators.map( ( indicator, i ) => {
|
||||||
|
const primaryItem = find( primaryData.data, data => data.stat === indicator.stat );
|
||||||
|
const secondaryItem = find( secondaryData.data, data => data.stat === indicator.stat );
|
||||||
|
|
||||||
|
if ( ! primaryItem || ! secondaryItem ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const href =
|
||||||
|
( primaryItem._links &&
|
||||||
|
primaryItem._links.report[ 0 ] &&
|
||||||
|
primaryItem._links.report[ 0 ].href ) ||
|
||||||
|
'';
|
||||||
|
const reportUrl =
|
||||||
|
( href && getNewPath( persistedQuery, href, { chart: primaryItem.chart } ) ) || '';
|
||||||
|
const delta = calculateDelta( primaryItem.value, secondaryItem.value );
|
||||||
|
const primaryValue = formatValue( primaryItem.format, primaryItem.value );
|
||||||
|
const secondaryValue = formatValue( secondaryItem.format, secondaryItem.value );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SummaryNumber
|
||||||
|
key={ i }
|
||||||
|
href={ reportUrl }
|
||||||
|
label={ indicator.label }
|
||||||
|
value={ primaryValue }
|
||||||
|
prevLabel={ prevLabel }
|
||||||
|
prevValue={ secondaryValue }
|
||||||
|
delta={ delta }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} ) }
|
||||||
|
</SummaryList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<SectionHeader title={ __( 'Store Performance', 'wc-admin' ) } menu={ this.renderMenu() } />
|
<SectionHeader title={ __( 'Store Performance', 'wc-admin' ) } menu={ this.renderMenu() } />
|
||||||
<Card className="woocommerce-dashboard__store-performance">
|
<Card className="woocommerce-dashboard__store-performance">{ this.renderList() }</Card>
|
||||||
<SummaryList>
|
|
||||||
{ showCustomers && (
|
|
||||||
<SummaryNumber
|
|
||||||
label={ __( 'New Customers', 'wc-admin' ) }
|
|
||||||
value={ '2' }
|
|
||||||
prevLabel={ __( 'Previous Week:', 'wc-admin' ) }
|
|
||||||
prevValue={ 3 }
|
|
||||||
delta={ -33 }
|
|
||||||
/>
|
|
||||||
) }
|
|
||||||
{ showProducts && (
|
|
||||||
<SummaryNumber
|
|
||||||
label={ __( 'Total Products', 'wc-admin' ) }
|
|
||||||
value={ totalProducts }
|
|
||||||
prevLabel={ __( 'Previous Week:', 'wc-admin' ) }
|
|
||||||
prevValue={ totalProducts }
|
|
||||||
delta={ 0 }
|
|
||||||
/>
|
|
||||||
) }
|
|
||||||
{ showOrders && (
|
|
||||||
<SummaryNumber
|
|
||||||
label={ __( 'Total Orders', 'wc-admin' ) }
|
|
||||||
value={ totalOrders }
|
|
||||||
prevLabel={ __( 'Previous Week:', 'wc-admin' ) }
|
|
||||||
prevValue={ totalOrders }
|
|
||||||
delta={ 0 }
|
|
||||||
/>
|
|
||||||
) }
|
|
||||||
</SummaryList>
|
|
||||||
</Card>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default compose(
|
||||||
|
withSelect( ( select, props ) => {
|
||||||
|
const { query } = props;
|
||||||
|
const {
|
||||||
|
getCurrentUserData,
|
||||||
|
getReportItems,
|
||||||
|
getReportItemsError,
|
||||||
|
isReportItemsRequesting,
|
||||||
|
} = select( 'wc-api' );
|
||||||
|
const userData = getCurrentUserData();
|
||||||
|
let userPrefs = userData.dashboard_performance_indicators;
|
||||||
|
|
||||||
export default StorePerformance;
|
// Set default values for user preferences if none is set.
|
||||||
|
// These columns are HIDDEN by default.
|
||||||
|
if ( ! userPrefs ) {
|
||||||
|
userPrefs = [ 'taxes/order_tax', 'taxes/shipping_tax', 'downloads/download_count' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
const datesFromQuery = getCurrentDates( query );
|
||||||
|
const endPrimary = datesFromQuery.primary.before;
|
||||||
|
const endSecondary = datesFromQuery.secondary.before;
|
||||||
|
|
||||||
|
const indicators = wcSettings.dataEndpoints.performanceIndicators;
|
||||||
|
const userIndicators = indicators.filter( indicator => ! userPrefs.includes( indicator.stat ) );
|
||||||
|
const statKeys = userIndicators.map( indicator => indicator.stat ).join( ',' );
|
||||||
|
|
||||||
|
const primaryQuery = {
|
||||||
|
after: appendTimestamp( datesFromQuery.primary.after, 'start' ),
|
||||||
|
before: appendTimestamp( endPrimary, endPrimary.isSame( moment(), 'day' ) ? 'now' : 'end' ),
|
||||||
|
stats: statKeys,
|
||||||
|
};
|
||||||
|
|
||||||
|
const secondaryQuery = {
|
||||||
|
after: appendTimestamp( datesFromQuery.secondary.after, 'start' ),
|
||||||
|
before: appendTimestamp(
|
||||||
|
endSecondary,
|
||||||
|
endSecondary.isSame( moment(), 'day' ) ? 'now' : 'end'
|
||||||
|
),
|
||||||
|
stats: statKeys,
|
||||||
|
};
|
||||||
|
|
||||||
|
const primaryData = getReportItems( 'performance-indicators', primaryQuery );
|
||||||
|
const primaryError = getReportItemsError( 'performance-indicators', primaryQuery ) || null;
|
||||||
|
const primaryRequesting = isReportItemsRequesting( 'performance-indicators', primaryQuery );
|
||||||
|
|
||||||
|
const secondaryData = getReportItems( 'performance-indicators', secondaryQuery );
|
||||||
|
const secondaryError = getReportItemsError( 'performance-indicators', secondaryQuery ) || null;
|
||||||
|
const secondaryRequesting = isReportItemsRequesting( 'performance-indicators', secondaryQuery );
|
||||||
|
|
||||||
|
return {
|
||||||
|
userPrefs,
|
||||||
|
userIndicators,
|
||||||
|
indicators,
|
||||||
|
primaryData,
|
||||||
|
primaryError,
|
||||||
|
primaryRequesting,
|
||||||
|
secondaryData,
|
||||||
|
secondaryError,
|
||||||
|
secondaryRequesting,
|
||||||
|
};
|
||||||
|
} ),
|
||||||
|
withDispatch( dispatch => {
|
||||||
|
const { updateCurrentUserData } = dispatch( 'wc-api' );
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateCurrentUserData,
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
)( StorePerformance );
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { get } from 'lodash';
|
import { get, isFinite } from 'lodash';
|
||||||
const number_format = require( 'locutus/php/strings/number_format' );
|
const number_format = require( 'locutus/php/strings/number_format' );
|
||||||
|
import { formatCurrency } from '@woocommerce/currency';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a number using site's current locale
|
* Formats a number using site's current locale
|
||||||
|
@ -13,7 +14,6 @@ const number_format = require( 'locutus/php/strings/number_format' );
|
||||||
* @param {int|null} [precision=null] optional decimal precision
|
* @param {int|null} [precision=null] optional decimal precision
|
||||||
* @returns {?String} A formatted string.
|
* @returns {?String} A formatted string.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function numberFormat( number, precision = null ) {
|
export function numberFormat( number, precision = null ) {
|
||||||
if ( 'number' !== typeof number ) {
|
if ( 'number' !== typeof number ) {
|
||||||
number = parseFloat( number );
|
number = parseFloat( number );
|
||||||
|
@ -34,3 +34,30 @@ export function numberFormat( number, precision = null ) {
|
||||||
|
|
||||||
return number_format( number, precision, decimalSeparator, thousandSeparator );
|
return number_format( number, precision, decimalSeparator, thousandSeparator );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatValue( type, value ) {
|
||||||
|
if ( ! isFinite( value ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( type ) {
|
||||||
|
case 'average':
|
||||||
|
return Math.round( value );
|
||||||
|
case 'currency':
|
||||||
|
return formatCurrency( value );
|
||||||
|
case 'number':
|
||||||
|
return numberFormat( value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateDelta( primaryValue, secondaryValue ) {
|
||||||
|
if ( ! isFinite( primaryValue ) || ! isFinite( secondaryValue ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( secondaryValue === 0 ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round( ( primaryValue - secondaryValue ) / secondaryValue * 100 );
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ const typeEndpointMap = {
|
||||||
'report-items-query-downloads': 'downloads',
|
'report-items-query-downloads': 'downloads',
|
||||||
'report-items-query-customers': 'customers',
|
'report-items-query-customers': 'customers',
|
||||||
'report-items-query-stock': 'stock',
|
'report-items-query-stock': 'stock',
|
||||||
|
'report-items-query-performance-indicators': 'performance-indicators',
|
||||||
};
|
};
|
||||||
|
|
||||||
function read( resourceNames, fetch = apiFetch ) {
|
function read( resourceNames, fetch = apiFetch ) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ function updateCurrentUserData( resourceNames, data, fetch ) {
|
||||||
'revenue_report_columns',
|
'revenue_report_columns',
|
||||||
'taxes_report_columns',
|
'taxes_report_columns',
|
||||||
'variations_report_columns',
|
'variations_report_columns',
|
||||||
|
'dashboard_performance_indicators',
|
||||||
'dashboard_charts',
|
'dashboard_charts',
|
||||||
'dashboard_chart_type',
|
'dashboard_chart_type',
|
||||||
'dashboard_chart_interval',
|
'dashboard_chart_interval',
|
||||||
|
|
|
@ -64,6 +64,7 @@ class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all reports.
|
* Get all reports.
|
||||||
*
|
*
|
||||||
|
@ -135,7 +136,36 @@ class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list of allowed reports, so that data can be loaded from third party extensions in addition to WooCommerce core.
|
||||||
|
* Array items should be in format of array( 'slug' => 'downloads/stats', 'description' => '',
|
||||||
|
* 'url' => '', and 'path' => '/wc-ext/v1/...'.
|
||||||
|
*
|
||||||
|
* @param array $endpoints The list of allowed reports..
|
||||||
|
*/
|
||||||
|
$reports = apply_filters( 'woocommerce_admin_reports', $reports );
|
||||||
|
|
||||||
foreach ( $reports as $report ) {
|
foreach ( $reports as $report ) {
|
||||||
|
if ( empty( $report['slug'] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $report['path'] ) ) {
|
||||||
|
$report['path'] = '/' . $this->namespace . '/reports/' . $report['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows a different admin page to be loaded here,
|
||||||
|
// or allows an empty url if no report exists for a set of performance indicators.
|
||||||
|
if ( ! isset( $report['url'] ) ) {
|
||||||
|
if ( '/stats' === substr( $report['slug'], -6 ) ) {
|
||||||
|
$url_slug = substr( $report['slug'], 0, -6 );
|
||||||
|
} else {
|
||||||
|
$url_slug = $report['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$report['url'] = '/analytics/' . $url_slug;
|
||||||
|
}
|
||||||
|
|
||||||
$item = $this->prepare_item_for_response( (object) $report, $request );
|
$item = $this->prepare_item_for_response( (object) $report, $request );
|
||||||
$data[] = $this->prepare_response_for_collection( $item );
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
}
|
}
|
||||||
|
@ -154,6 +184,7 @@ class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
$data = array(
|
$data = array(
|
||||||
'slug' => $report->slug,
|
'slug' => $report->slug,
|
||||||
'description' => $report->description,
|
'description' => $report->description,
|
||||||
|
'path' => $report->path,
|
||||||
);
|
);
|
||||||
|
|
||||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||||
|
@ -165,7 +196,10 @@ class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
$response->add_links(
|
$response->add_links(
|
||||||
array(
|
array(
|
||||||
'self' => array(
|
'self' => array(
|
||||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $report->slug ) ),
|
'href' => rest_url( $report->path ),
|
||||||
|
),
|
||||||
|
'report' => array(
|
||||||
|
'href' => $report->url,
|
||||||
),
|
),
|
||||||
'collection' => array(
|
'collection' => array(
|
||||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||||
|
@ -208,6 +242,12 @@ class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
'context' => array( 'view' ),
|
'context' => array( 'view' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
),
|
),
|
||||||
|
'path' => array(
|
||||||
|
'description' => __( 'API path.', 'wc-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,8 @@ class WC_Admin_REST_Reports_Coupons_Stats_Controller extends WC_REST_Reports_Con
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'coupons_count' => array(
|
'coupons_count' => array(
|
||||||
'description' => __( 'Amount of coupons.', 'wc-admin' ),
|
'description' => __( 'Amount of coupons.', 'wc-admin' ),
|
||||||
|
@ -146,10 +148,11 @@ class WC_Admin_REST_Reports_Coupons_Stats_Controller extends WC_REST_Reports_Con
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
),
|
),
|
||||||
'orders_count' => array(
|
'orders_count' => array(
|
||||||
'description' => __( 'Amount of orders.', 'wc-admin' ),
|
'description' => __( 'Amount of discounted orders.', 'wc-admin' ),
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ class WC_Admin_REST_Reports_Downloads_Stats_Controller extends WC_REST_Reports_C
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -146,18 +146,22 @@ class WC_Admin_REST_Reports_Orders_Stats_Controller extends WC_Admin_REST_Report
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
),
|
'format' => 'currency',
|
||||||
'avg_order_value' => array(
|
|
||||||
'description' => __( 'Average order value.', 'wc-admin' ),
|
|
||||||
'type' => 'number',
|
|
||||||
'context' => array( 'view', 'edit' ),
|
|
||||||
'readonly' => true,
|
|
||||||
),
|
),
|
||||||
'orders_count' => array(
|
'orders_count' => array(
|
||||||
'description' => __( 'Amount of orders', 'wc-admin' ),
|
'description' => __( 'Amount of orders', 'wc-admin' ),
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
),
|
||||||
|
'avg_order_value' => array(
|
||||||
|
'description' => __( 'Average order value.', 'wc-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'avg_items_per_order' => array(
|
'avg_items_per_order' => array(
|
||||||
'description' => __( 'Average items per order', 'wc-admin' ),
|
'description' => __( 'Average items per order', 'wc-admin' ),
|
||||||
|
|
|
@ -31,6 +31,67 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
*/
|
*/
|
||||||
protected $rest_base = 'reports/performance-indicators';
|
protected $rest_base = 'reports/performance-indicators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of endpoints by report slug.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $endpoints = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of allowed stats.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $allowed_stats = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of stat labels.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $labels = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of endpoints by url.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $urls = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the routes for reports.
|
||||||
|
*/
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base,
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_items' ),
|
||||||
|
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||||
|
'args' => $this->get_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/allowed',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_allowed_items' ),
|
||||||
|
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||||
|
'args' => $this->get_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_allowed_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps query arguments from the REST request.
|
* Maps query arguments from the REST request.
|
||||||
*
|
*
|
||||||
|
@ -46,12 +107,15 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all allowed stats that can be returned from this endpoint.
|
* Get information such as allowed stats, stat labels, and endpoint data from stats reports.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return WP_Error|True
|
||||||
*/
|
*/
|
||||||
public function get_allowed_stats() {
|
private function get_indicator_data() {
|
||||||
global $wp_rest_server;
|
// Data already retrieved.
|
||||||
|
if ( ! empty( $this->endpoints ) && ! empty( $this->labels ) && ! empty( $this->allowed_stats ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$request = new WP_REST_Request( 'GET', '/wc/v4/reports' );
|
$request = new WP_REST_Request( 'GET', '/wc/v4/reports' );
|
||||||
$response = rest_do_request( $request );
|
$response = rest_do_request( $request );
|
||||||
|
@ -63,9 +127,10 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
|
|
||||||
foreach ( $endpoints as $endpoint ) {
|
foreach ( $endpoints as $endpoint ) {
|
||||||
if ( '/stats' === substr( $endpoint['slug'], -6 ) ) {
|
if ( '/stats' === substr( $endpoint['slug'], -6 ) ) {
|
||||||
$request = new WP_REST_Request( 'OPTIONS', '/wc/v4/reports/' . $endpoint['slug'] );
|
$request = new WP_REST_Request( 'OPTIONS', $endpoint['path'] );
|
||||||
$response = rest_do_request( $request );
|
$response = rest_do_request( $request );
|
||||||
$data = $response->get_data();
|
$data = $response->get_data();
|
||||||
|
|
||||||
$prefix = substr( $endpoint['slug'], 0, -6 );
|
$prefix = substr( $endpoint['slug'], 0, -6 );
|
||||||
|
|
||||||
if ( empty( $data['schema']['properties']['totals']['properties'] ) ) {
|
if ( empty( $data['schema']['properties']['totals']['properties'] ) ) {
|
||||||
|
@ -73,17 +138,113 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $data['schema']['properties']['totals']['properties'] as $property_key => $schema_info ) {
|
foreach ( $data['schema']['properties']['totals']['properties'] as $property_key => $schema_info ) {
|
||||||
$allowed_stats[] = $prefix . '/' . $property_key;
|
if ( empty( $schema_info['indicator'] ) || ! $schema_info['indicator'] ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stat = $prefix . '/' . $property_key;
|
||||||
|
$allowed_stats[] = $stat;
|
||||||
|
|
||||||
|
$this->labels[ $stat ] = trim( preg_replace( '/\W+/', ' ', $schema_info['description'] ) );
|
||||||
|
$this->formats[ $stat ] = isset( $schema_info['format'] ) ? $schema_info['format'] : 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->endpoints[ $prefix ] = $endpoint['path'];
|
||||||
|
$this->urls[ $prefix ] = $endpoint['_links']['report'][0]['href'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->allowed_stats = $allowed_stats;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of allowed performance indicators.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_allowed_items( $request ) {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
return $indicator_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach ( $this->allowed_stats as $stat ) {
|
||||||
|
$pieces = $this->get_stats_parts( $stat );
|
||||||
|
$report = $pieces[0];
|
||||||
|
$chart = $pieces[1];
|
||||||
|
$data[] = (object) array(
|
||||||
|
'stat' => $stat,
|
||||||
|
'chart' => $chart,
|
||||||
|
'label' => $this->labels[ $stat ],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
usort( $data, array( $this, 'sort' ) );
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
foreach ( $data as $item ) {
|
||||||
|
$prepared = $this->prepare_item_for_response( $item, $request );
|
||||||
|
$objects[] = $this->prepare_response_for_collection( $prepared );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $objects );
|
||||||
|
$response->header( 'X-WP-Total', count( $data ) );
|
||||||
|
$response->header( 'X-WP-TotalPages', 1 );
|
||||||
|
|
||||||
|
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the list of stats. Sorted by custom arrangement.
|
||||||
|
*
|
||||||
|
* @see https://github.com/woocommerce/wc-admin/issues/1282
|
||||||
|
* @param object $a First item.
|
||||||
|
* @param object $b Second item.
|
||||||
|
* @return order
|
||||||
|
*/
|
||||||
|
public function sort( $a, $b ) {
|
||||||
/**
|
/**
|
||||||
* Filter the list of allowed stats that can be returned via the performance indiciator endpoint.
|
* Custom ordering for store performance indicators.
|
||||||
*
|
*
|
||||||
* @param array $allowed_stats The list of allowed stats.
|
* @see https://github.com/woocommerce/wc-admin/issues/1282
|
||||||
|
* @param array $indicators A list of ordered indicators.
|
||||||
*/
|
*/
|
||||||
return apply_filters( 'woocommerce_admin_performance_indicators_allowed_stats', $allowed_stats );
|
$stat_order = apply_filters(
|
||||||
|
'woocommerce_rest_report_sort_performance_indicators',
|
||||||
|
array(
|
||||||
|
'revenue/gross_revenue',
|
||||||
|
'revenue/net_revenue',
|
||||||
|
'orders/orders_count',
|
||||||
|
'orders/avg_order_value',
|
||||||
|
'products/items_sold',
|
||||||
|
'revenue/refunds',
|
||||||
|
'coupons/orders_count',
|
||||||
|
'coupons/amount',
|
||||||
|
'taxes/total_tax',
|
||||||
|
'taxes/order_tax',
|
||||||
|
'taxes/shipping_tax',
|
||||||
|
'revenue/shipping',
|
||||||
|
'downloads/download_count',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$a = array_search( $a->stat, $stat_order );
|
||||||
|
$b = array_search( $b->stat, $stat_order );
|
||||||
|
|
||||||
|
if ( false === $a && false === $b ) {
|
||||||
|
return 0;
|
||||||
|
} elseif ( false === $a ) {
|
||||||
|
return 1;
|
||||||
|
} elseif ( false === $b ) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return $a - $b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,10 +254,9 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
* @return array|WP_Error
|
* @return array|WP_Error
|
||||||
*/
|
*/
|
||||||
public function get_items( $request ) {
|
public function get_items( $request ) {
|
||||||
$allowed_stats = $this->get_allowed_stats();
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
if ( is_wp_error( $allowed_stats ) ) {
|
return $indicator_data;
|
||||||
return $allowed_stats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$query_args = $this->prepare_reports_query( $request );
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
@ -105,51 +265,50 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
}
|
}
|
||||||
|
|
||||||
$stats = array();
|
$stats = array();
|
||||||
foreach ( $query_args['stats'] as $stat_request ) {
|
foreach ( $query_args['stats'] as $stat ) {
|
||||||
$is_error = false;
|
$is_error = false;
|
||||||
|
|
||||||
$pieces = explode( '/', $stat_request );
|
$pieces = $this->get_stats_parts( $stat );
|
||||||
$endpoint = $pieces[0];
|
$report = $pieces[0];
|
||||||
$stat = $pieces[1];
|
$chart = $pieces[1];
|
||||||
|
|
||||||
if ( ! in_array( $stat_request, $allowed_stats ) ) {
|
if ( ! in_array( $stat, $this->allowed_stats ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$request_url = $this->endpoints[ $report ];
|
||||||
* Filter the list of allowed endpoints, so that data can be loaded from extensions rather than core.
|
$request = new WP_REST_Request( 'GET', $request_url );
|
||||||
* These should be in the format of slug => path. Example: `bookings` => `/wc-bookings/v1/reports/bookings/stats`.
|
|
||||||
*
|
|
||||||
* @param array $endpoints The list of allowed endpoints.
|
|
||||||
*/
|
|
||||||
$stats_endpoints = apply_filters( 'woocommerce_admin_performance_indicators_stats_endpoints', array() );
|
|
||||||
if ( ! empty( $stats_endpoints [ $endpoint ] ) ) {
|
|
||||||
$request_url = $stats_endpoints [ $endpoint ];
|
|
||||||
} else {
|
|
||||||
$request_url = '/wc/v4/reports/' . $endpoint . '/stats';
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = new WP_REST_Request( 'GET', $request_url );
|
|
||||||
$request->set_param( 'before', $query_args['before'] );
|
$request->set_param( 'before', $query_args['before'] );
|
||||||
$request->set_param( 'after', $query_args['after'] );
|
$request->set_param( 'after', $query_args['after'] );
|
||||||
|
|
||||||
$response = rest_do_request( $request );
|
$response = rest_do_request( $request );
|
||||||
$data = $response->get_data();
|
|
||||||
|
|
||||||
if ( 200 !== $response->get_status() || empty( $data['totals'][ $stat ] ) ) {
|
$data = $response->get_data();
|
||||||
|
$format = $this->formats[ $stat ];
|
||||||
|
$label = $this->labels[ $stat ];
|
||||||
|
|
||||||
|
if ( 200 !== $response->get_status() || ! isset( $data['totals'][ $chart ] ) ) {
|
||||||
$stats[] = (object) array(
|
$stats[] = (object) array(
|
||||||
'stat' => $stat_request,
|
'stat' => $stat,
|
||||||
'value' => null,
|
'chart' => $chart,
|
||||||
|
'label' => $label,
|
||||||
|
'format' => $format,
|
||||||
|
'value' => null,
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stats[] = (object) array(
|
$stats[] = (object) array(
|
||||||
'stat' => $stat_request,
|
'stat' => $stat,
|
||||||
'value' => $data['totals'][ $stat ],
|
'chart' => $chart,
|
||||||
|
'label' => $label,
|
||||||
|
'format' => $format,
|
||||||
|
'value' => $data['totals'][ $chart ],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usort( $stats, array( $this, 'sort' ) );
|
||||||
|
|
||||||
$objects = array();
|
$objects = array();
|
||||||
foreach ( $stats as $stat ) {
|
foreach ( $stats as $stat ) {
|
||||||
$data = $this->prepare_item_for_response( $stat, $request );
|
$data = $this->prepare_item_for_response( $stat, $request );
|
||||||
|
@ -201,25 +360,52 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function prepare_links( $object ) {
|
protected function prepare_links( $object ) {
|
||||||
$pieces = explode( '/', $object->stat );
|
$pieces = $this->get_stats_parts( $object->stat );
|
||||||
$endpoint = $pieces[0];
|
$endpoint = $pieces[0];
|
||||||
$stat = $pieces[1];
|
$stat = $pieces[1];
|
||||||
|
$url = $this->urls[ $endpoint ];
|
||||||
|
|
||||||
$links = array(
|
$links = array(
|
||||||
|
'api' => array(
|
||||||
|
'href' => rest_url( $this->endpoints[ $endpoint ] ),
|
||||||
|
),
|
||||||
'report' => array(
|
'report' => array(
|
||||||
'href' => rest_url( sprintf( '/%s/reports/%s/stats', $this->namespace, $endpoint ) ),
|
'href' => ! empty( $url ) ? $url : '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $links;
|
return $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the endpoint part of a stat request (prefix) and the actual stat total we want.
|
||||||
|
* To allow extensions to namespace (example: fue/emails/sent), we break on the last forward slash.
|
||||||
|
*
|
||||||
|
* @param string $full_stat A stat request string like orders/avg_order_value or fue/emails/sent.
|
||||||
|
* @return array Containing the prefix (endpoint) and suffix (stat).
|
||||||
|
*/
|
||||||
|
private function get_stats_parts( $full_stat ) {
|
||||||
|
$endpoint = substr( $full_stat, 0, strrpos( $full_stat, '/' ) );
|
||||||
|
$stat = substr( $full_stat, ( strrpos( $full_stat, '/' ) + 1 ) );
|
||||||
|
return array(
|
||||||
|
$endpoint,
|
||||||
|
$stat,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Report's schema, conforming to JSON Schema.
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function get_item_schema() {
|
public function get_item_schema() {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
$allowed_stats = array();
|
||||||
|
} else {
|
||||||
|
$allowed_stats = $this->allowed_stats;
|
||||||
|
}
|
||||||
|
|
||||||
$schema = array(
|
$schema = array(
|
||||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
'title' => 'report_performance_indicator',
|
'title' => 'report_performance_indicator',
|
||||||
|
@ -230,6 +416,26 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'enum' => $allowed_stats,
|
||||||
|
),
|
||||||
|
'chart' => array(
|
||||||
|
'description' => __( 'The specific chart this stat referrers to.', 'wc-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'label' => array(
|
||||||
|
'description' => __( 'Human readable label for the stat.', 'wc-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'format' => array(
|
||||||
|
'description' => __( 'Format of the stat.', 'wc-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'number', 'currency' ),
|
||||||
),
|
),
|
||||||
'value' => array(
|
'value' => array(
|
||||||
'description' => __( 'Value of the stat. Returns null if the stat does not exist or cannot be loaded.', 'wc-admin' ),
|
'description' => __( 'Value of the stat. Returns null if the stat does not exist or cannot be loaded.', 'wc-admin' ),
|
||||||
|
@ -243,17 +449,29 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re
|
||||||
return $this->add_additional_fields_schema( $schema );
|
return $this->add_additional_fields_schema( $schema );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get schema for the list of allowed performance indicators.
|
||||||
|
*
|
||||||
|
* @return array $schema
|
||||||
|
*/
|
||||||
|
public function get_public_allowed_item_schema() {
|
||||||
|
$schema = $this->get_public_item_schema();
|
||||||
|
unset( $schema['properties']['value'] );
|
||||||
|
unset( $schema['properties']['format'] );
|
||||||
|
return $sceham;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the query params for collections.
|
* Get the query params for collections.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function get_collection_params() {
|
public function get_collection_params() {
|
||||||
$allowed_stats = $this->get_allowed_stats();
|
$indicator_data = $this->get_indicator_data();
|
||||||
if ( is_wp_error( $allowed_stats ) ) {
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
$allowed_stats = __( 'There was an issue loading the report endpoints', 'wc-admin' );
|
$allowed_stats = __( 'There was an issue loading the report endpoints', 'wc-admin' );
|
||||||
} else {
|
} else {
|
||||||
$allowed_stats = implode( ', ', $allowed_stats );
|
$allowed_stats = implode( ', ', $this->allowed_stats );
|
||||||
}
|
}
|
||||||
|
|
||||||
$params = array();
|
$params = array();
|
||||||
|
|
|
@ -152,12 +152,14 @@ class WC_Admin_REST_Reports_Products_Stats_Controller extends WC_REST_Reports_Co
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
),
|
),
|
||||||
'net_revenue' => array(
|
'net_revenue' => array(
|
||||||
'description' => __( 'Net revenue.', 'wc-admin' ),
|
'description' => __( 'Net revenue.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'orders_count' => array(
|
'orders_count' => array(
|
||||||
'description' => __( 'Number of orders.', 'wc-admin' ),
|
'description' => __( 'Number of orders.', 'wc-admin' ),
|
||||||
|
|
|
@ -136,51 +136,61 @@ class WC_Admin_REST_Reports_Revenue_Stats_Controller extends WC_REST_Reports_Con
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'net_revenue' => array(
|
'net_revenue' => array(
|
||||||
'description' => __( 'Net revenue.', 'wc-admin' ),
|
'description' => __( 'Net revenue.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'coupons' => array(
|
'coupons' => array(
|
||||||
'description' => __( 'Total of coupons.', 'wc-admin' ),
|
'description' => __( 'Total of coupons.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'shipping' => array(
|
'shipping' => array(
|
||||||
'description' => __( 'Total of shipping.', 'wc-admin' ),
|
'description' => __( 'Total of shipping.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'taxes' => array(
|
'taxes' => array(
|
||||||
'description' => __( 'Total of taxes.', 'wc-admin' ),
|
'description' => __( 'Total of taxes.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'refunds' => array(
|
'refunds' => array(
|
||||||
'description' => __( 'Total of refunds.', 'wc-admin' ),
|
'description' => __( 'Total of refunds.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'orders_count' => array(
|
'orders_count' => array(
|
||||||
'description' => __( 'Amount of orders', 'wc-admin' ),
|
'description' => __( 'Amount of orders.', 'wc-admin' ),
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
),
|
),
|
||||||
'num_items_sold' => array(
|
'num_items_sold' => array(
|
||||||
'description' => __( 'Amount of orders', 'wc-admin' ),
|
'description' => __( 'Items sold.', 'wc-admin' ),
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
),
|
),
|
||||||
'products' => array(
|
'products' => array(
|
||||||
'description' => __( 'Amount of orders', 'wc-admin' ),
|
'description' => __( 'Products sold.', 'wc-admin' ),
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
|
|
@ -167,18 +167,24 @@ class WC_Admin_REST_Reports_Taxes_Stats_Controller extends WC_REST_Reports_Contr
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'order_tax' => array(
|
'order_tax' => array(
|
||||||
'description' => __( 'Order tax.', 'wc-admin' ),
|
'description' => __( 'Order tax.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'shipping_tax' => array(
|
'shipping_tax' => array(
|
||||||
'description' => __( 'Shipping tax.', 'wc-admin' ),
|
'description' => __( 'Shipping tax.', 'wc-admin' ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'context' => array( 'view', 'edit' ),
|
'context' => array( 'view', 'edit' ),
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
),
|
),
|
||||||
'orders_count' => array(
|
'orders_count' => array(
|
||||||
'description' => __( 'Amount of orders.', 'wc-admin' ),
|
'description' => __( 'Amount of orders.', 'wc-admin' ),
|
||||||
|
|
|
@ -399,6 +399,7 @@ function wc_admin_get_user_data_fields() {
|
||||||
'revenue_report_columns',
|
'revenue_report_columns',
|
||||||
'taxes_report_columns',
|
'taxes_report_columns',
|
||||||
'variations_report_columns',
|
'variations_report_columns',
|
||||||
|
'dashboard_performance_indicators',
|
||||||
'dashboard_charts',
|
'dashboard_charts',
|
||||||
'dashboard_chart_type',
|
'dashboard_chart_type',
|
||||||
'dashboard_chart_interval',
|
'dashboard_chart_interval',
|
||||||
|
|
|
@ -153,7 +153,8 @@ function wc_admin_print_script_settings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$preload_data_endpoints = array(
|
$preload_data_endpoints = array(
|
||||||
'countries' => '/wc/v4/data/countries',
|
'countries' => '/wc/v4/data/countries',
|
||||||
|
'performanceIndicators' => '/wc/v4/reports/performance-indicators/allowed',
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( function_exists( 'gutenberg_preload_api_request' ) ) {
|
if ( function_exists( 'gutenberg_preload_api_request' ) ) {
|
||||||
|
@ -169,7 +170,7 @@ function wc_admin_print_script_settings() {
|
||||||
|
|
||||||
$current_user_data = array();
|
$current_user_data = array();
|
||||||
foreach ( wc_admin_get_user_data_fields() as $user_field ) {
|
foreach ( wc_admin_get_user_data_fields() as $user_field ) {
|
||||||
$current_user_data[ $user_field ] = get_user_meta( get_current_user_id(), 'wc_admin_' . $user_field, true );
|
$current_user_data[ $user_field ] = json_decode( get_user_meta( get_current_user_id(), 'wc_admin_' . $user_field, true ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,22 +180,22 @@ function wc_admin_print_script_settings() {
|
||||||
|
|
||||||
// Settings and variables can be passed here for access in the app.
|
// Settings and variables can be passed here for access in the app.
|
||||||
$settings = array(
|
$settings = array(
|
||||||
'adminUrl' => admin_url(),
|
'adminUrl' => admin_url(),
|
||||||
'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ),
|
'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ),
|
||||||
'wcAdminAssetUrl' => plugins_url( 'images/', wc_admin_dir_path( 'wc-admin.php' ) ), // Temporary for plugin. See above.
|
'wcAdminAssetUrl' => plugins_url( 'images/', wc_admin_dir_path( 'wc-admin.php' ) ), // Temporary for plugin. See above.
|
||||||
'embedBreadcrumbs' => wc_admin_get_embed_breadcrumbs(),
|
'embedBreadcrumbs' => wc_admin_get_embed_breadcrumbs(),
|
||||||
'siteLocale' => esc_attr( get_bloginfo( 'language' ) ),
|
'siteLocale' => esc_attr( get_bloginfo( 'language' ) ),
|
||||||
'currency' => wc_admin_currency_settings(),
|
'currency' => wc_admin_currency_settings(),
|
||||||
'orderStatuses' => format_order_statuses( wc_get_order_statuses() ),
|
'orderStatuses' => format_order_statuses( wc_get_order_statuses() ),
|
||||||
'stockStatuses' => wc_get_product_stock_status_options(),
|
'stockStatuses' => wc_get_product_stock_status_options(),
|
||||||
'siteTitle' => get_bloginfo( 'name' ),
|
'siteTitle' => get_bloginfo( 'name' ),
|
||||||
'trackingEnabled' => $tracking_enabled,
|
'trackingEnabled' => $tracking_enabled,
|
||||||
'dataEndpoints' => array(),
|
'dataEndpoints' => array(),
|
||||||
'l10n' => array(
|
'l10n' => array(
|
||||||
'userLocale' => get_user_locale(),
|
'userLocale' => get_user_locale(),
|
||||||
'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
|
'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
|
||||||
),
|
),
|
||||||
'currentUserData' => $current_user_data,
|
'currentUserData' => $current_user_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $preload_data_endpoints as $key => $endpoint ) {
|
foreach ( $preload_data_endpoints as $key => $endpoint ) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# 1.4.1 (unreleased)
|
# 1.4.1 (unreleased)
|
||||||
- Chart component: format numbers and prices using store currency settings.
|
- Chart component: format numbers and prices using store currency settings.
|
||||||
|
- Make `href`/linking optional in SummaryNumber.
|
||||||
|
|
||||||
# 1.4.0
|
# 1.4.0
|
||||||
- Add download log ip address autocompleter to search component
|
- Add download log ip address autocompleter to search component
|
||||||
|
|
|
@ -52,17 +52,23 @@ const SummaryNumber = ( {
|
||||||
screenReaderLabel = sprintf( __( 'No change from %s', 'wc-admin' ), prevLabel );
|
screenReaderLabel = sprintf( __( 'No change from %s', 'wc-admin' ), prevLabel );
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = onToggle ? Button : Link;
|
let Container;
|
||||||
const containerProps = {
|
const containerProps = {
|
||||||
className: classes,
|
className: classes,
|
||||||
'aria-current': selected ? 'page' : null,
|
'aria-current': selected ? 'page' : null,
|
||||||
};
|
};
|
||||||
if ( ! onToggle ) {
|
|
||||||
containerProps.href = href;
|
if ( onToggle || href ) {
|
||||||
containerProps.role = 'menuitem';
|
Container = onToggle ? Button : Link;
|
||||||
|
if ( ! onToggle ) {
|
||||||
|
containerProps.href = href;
|
||||||
|
containerProps.role = 'menuitem';
|
||||||
|
} else {
|
||||||
|
containerProps.onClick = onToggle;
|
||||||
|
containerProps[ 'aria-expanded' ] = isOpen;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
containerProps.onClick = onToggle;
|
Container = 'div';
|
||||||
containerProps[ 'aria-expanded' ] = isOpen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -109,7 +115,7 @@ SummaryNumber.propTypes = {
|
||||||
/**
|
/**
|
||||||
* An internal link to the report focused on this number.
|
* An internal link to the report focused on this number.
|
||||||
*/
|
*/
|
||||||
href: PropTypes.string.isRequired,
|
href: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* Boolean describing whether the menu list is open. Only applies in mobile view,
|
* Boolean describing whether the menu list is open. Only applies in mobile view,
|
||||||
* and only applies to the toggle-able item (first in the list).
|
* and only applies to the toggle-able item (first in the list).
|
||||||
|
@ -147,7 +153,7 @@ SummaryNumber.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
SummaryNumber.defaultProps = {
|
SummaryNumber.defaultProps = {
|
||||||
href: '/analytics',
|
href: '',
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
prevLabel: __( 'Previous Period:', 'wc-admin' ),
|
prevLabel: __( 'Previous Period:', 'wc-admin' ),
|
||||||
reverseTrend: false,
|
reverseTrend: false,
|
||||||
|
|
|
@ -37,6 +37,7 @@ class WC_Tests_API_Reports_Performance_Indicators extends WC_REST_Unit_Test_Case
|
||||||
$routes = $this->server->get_routes();
|
$routes = $this->server->get_routes();
|
||||||
|
|
||||||
$this->assertArrayHasKey( $this->endpoint, $routes );
|
$this->assertArrayHasKey( $this->endpoint, $routes );
|
||||||
|
$this->assertArrayHasKey( $this->endpoint . '/allowed', $routes );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,14 +97,19 @@ class WC_Tests_API_Reports_Performance_Indicators extends WC_REST_Unit_Test_Case
|
||||||
$reports = $response->get_data();
|
$reports = $response->get_data();
|
||||||
|
|
||||||
$this->assertEquals( 200, $response->get_status() );
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
|
||||||
$this->assertEquals( 2, count( $reports ) );
|
$this->assertEquals( 2, count( $reports ) );
|
||||||
|
|
||||||
$this->assertEquals( 'orders/orders_count', $reports[0]['stat'] );
|
$this->assertEquals( 'orders/orders_count', $reports[0]['stat'] );
|
||||||
|
$this->assertEquals( 'Amount of orders', $reports[0]['label'] );
|
||||||
$this->assertEquals( 1, $reports[0]['value'] );
|
$this->assertEquals( 1, $reports[0]['value'] );
|
||||||
|
$this->assertEquals( 'orders_count', $reports[0]['chart'] );
|
||||||
|
$this->assertEquals( '/analytics/orders', $response->data[0]['_links']['report'][0]['href'] );
|
||||||
|
|
||||||
$this->assertEquals( 'downloads/download_count', $reports[1]['stat'] );
|
$this->assertEquals( 'downloads/download_count', $reports[1]['stat'] );
|
||||||
|
$this->assertEquals( 'Number of downloads', $reports[1]['label'] );
|
||||||
$this->assertEquals( 2, $reports[1]['value'] );
|
$this->assertEquals( 2, $reports[1]['value'] );
|
||||||
|
$this->assertEquals( 'download_count', $reports[1]['chart'] );
|
||||||
|
$this->assertEquals( '/analytics/downloads', $response->data[1]['_links']['report'][0]['href'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,8 +154,11 @@ class WC_Tests_API_Reports_Performance_Indicators extends WC_REST_Unit_Test_Case
|
||||||
$data = $response->get_data();
|
$data = $response->get_data();
|
||||||
$properties = $data['schema']['properties'];
|
$properties = $data['schema']['properties'];
|
||||||
|
|
||||||
$this->assertEquals( 2, count( $properties ) );
|
$this->assertEquals( 5, count( $properties ) );
|
||||||
$this->assertArrayHasKey( 'stat', $properties );
|
$this->assertArrayHasKey( 'stat', $properties );
|
||||||
|
$this->assertArrayHasKey( 'chart', $properties );
|
||||||
|
$this->assertArrayHasKey( 'label', $properties );
|
||||||
|
$this->assertArrayHasKey( 'format', $properties );
|
||||||
$this->assertArrayHasKey( 'value', $properties );
|
$this->assertArrayHasKey( 'value', $properties );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue