From e835acc11b4df1493355fa39a0d56b298f80b1aa Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Thu, 17 Jan 2019 09:08:59 -0500 Subject: [PATCH] Hook up performance indicators to the REST API --- .../components/report-summary/index.js | 2 +- .../components/report-summary/utils.js | 42 ---- client/dashboard/index.js | 2 +- client/dashboard/store-performance/index.js | 235 +++++++++++++----- client/lib/number/index.js | 35 +++ client/wc-api/reports/items/operations.js | 1 + client/wc-api/user/operations.js | 1 + ...class-wc-admin-rest-reports-controller.php | 2 +- ...-rest-reports-coupons-stats-controller.php | 5 +- ...est-reports-downloads-stats-controller.php | 1 + ...n-rest-reports-orders-stats-controller.php | 16 +- ...orts-performance-indicators-controller.php | 172 ++++++++++++- ...rest-reports-products-stats-controller.php | 2 + ...-rest-reports-revenue-stats-controller.php | 16 +- ...in-rest-reports-taxes-stats-controller.php | 6 + lib/admin.php | 1 + lib/client-assets.php | 54 ++-- packages/components/CHANGELOG.md | 1 + packages/components/src/summary/number.js | 22 +- tests/api/reports-performance-indicators.php | 13 +- 20 files changed, 473 insertions(+), 156 deletions(-) delete mode 100644 client/analytics/components/report-summary/utils.js diff --git a/client/analytics/components/report-summary/index.js b/client/analytics/components/report-summary/index.js index fc59ca361d1..c163dfb13d5 100644 --- a/client/analytics/components/report-summary/index.js +++ b/client/analytics/components/report-summary/index.js @@ -19,7 +19,7 @@ import { SummaryList, SummaryListPlaceholder, SummaryNumber } from '@woocommerce */ import { getSummaryNumbers } from 'store/reports/utils'; 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'; /** diff --git a/client/analytics/components/report-summary/utils.js b/client/analytics/components/report-summary/utils.js deleted file mode 100644 index a4669da29c3..00000000000 --- a/client/analytics/components/report-summary/utils.js +++ /dev/null @@ -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 ); -} diff --git a/client/dashboard/index.js b/client/dashboard/index.js index bd200f5efdc..91457ab5139 100644 --- a/client/dashboard/index.js +++ b/client/dashboard/index.js @@ -22,7 +22,7 @@ export default class Dashboard extends Component {
- + diff --git a/client/dashboard/store-performance/index.js b/client/dashboard/store-performance/index.js index 849b226e740..049974d810e 100644 --- a/client/dashboard/store-performance/index.js +++ b/client/dashboard/store-performance/index.js @@ -2,9 +2,15 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { ToggleControl } from '@wordpress/components'; import { Component, Fragment } from '@wordpress/element'; +import { compose } from '@wordpress/compose'; +import { withDispatch } from '@wordpress/data'; +import { getCurrentDates, appendTimestamp, getDateParamsFromQuery } from '@woocommerce/date'; +import { getNewPath, getPersistedQuery } from '@woocommerce/navigation'; +import moment from 'moment'; +import { find } from 'lodash'; /** * Internal dependencies @@ -16,99 +22,196 @@ import { MenuTitle, SectionHeader, SummaryList, + SummaryListPlaceholder, SummaryNumber, } from '@woocommerce/components'; +import withSelect from 'wc-api/with-select'; import './style.scss'; +import { calculateDelta, formatValue } from 'lib/number'; class StorePerformance extends Component { - constructor() { - super( ...arguments ); + constructor( props ) { + super( props ); this.state = { - showCustomers: true, - showProducts: true, - showOrders: true, + userPrefs: props.userPrefs || [], }; - this.toggle = this.toggle.bind( this ); } - toggle( type ) { + toggle( statKey ) { 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() { + const { indicators } = this.props; return ( { __( 'Display Stats:', 'wc-admin' ) } - - - - - - - - - + { indicators.map( ( indicator, i ) => { + const checked = ! this.state.userPrefs.includes( indicator.stat ); + return ( + + + + ); + } ) } ); } - render() { - const totalOrders = 10; - const totalProducts = 1000; - const { showCustomers, showProducts, showOrders } = this.state; + renderList() { + const { + query, + primaryRequesting, + secondaryRequesting, + primaryError, + secondaryError, + primaryData, + secondaryData, + userIndicators, + } = this.props; + if ( primaryRequesting || secondaryRequesting ) { + return ; + } + + 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 ( + + { 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 ( + + ); + } ) } + + ); + } + + render() { return ( - - - { showCustomers && ( - - ) } - { showProducts && ( - - ) } - { showOrders && ( - - ) } - - + { this.renderList() } ); } } +export default compose( + withSelect( ( select, props ) => { + const { query } = props; + const { + getCurrentUserData, + getReportItems, + getReportItemsError, + isReportItemsRequesting, + } = select( 'wc-api' ); + const userData = getCurrentUserData(); + const userPrefs = userData.dashboard_performance_indicators; + + 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( ',' ); -export default StorePerformance; + 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 ); diff --git a/client/lib/number/index.js b/client/lib/number/index.js index 52a262f95c5..1217b3136d9 100644 --- a/client/lib/number/index.js +++ b/client/lib/number/index.js @@ -1,3 +1,11 @@ +/** @format */ + +/** + * External dependencies + */ +import { formatCurrency } from '@woocommerce/currency'; +import { isFinite } from 'lodash'; + /** * Formats a number using site's current locale * @@ -18,3 +26,30 @@ export function numberFormat( number ) { } return new Intl.NumberFormat( locale ).format( 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 ); +} diff --git a/client/wc-api/reports/items/operations.js b/client/wc-api/reports/items/operations.js index a850a119c60..ae7dd63f420 100644 --- a/client/wc-api/reports/items/operations.js +++ b/client/wc-api/reports/items/operations.js @@ -26,6 +26,7 @@ const typeEndpointMap = { 'report-items-query-downloads': 'downloads', 'report-items-query-customers': 'customers', 'report-items-query-stock': 'stock', + 'report-items-query-performance-indicators': 'performance-indicators', }; function read( resourceNames, fetch = apiFetch ) { diff --git a/client/wc-api/user/operations.js b/client/wc-api/user/operations.js index 2c036b1fadd..0e4bb407ca0 100644 --- a/client/wc-api/user/operations.js +++ b/client/wc-api/user/operations.js @@ -40,6 +40,7 @@ function updateCurrentUserData( resourceNames, data, fetch ) { 'revenue_report_columns', 'taxes_report_columns', 'variations_report_columns', + 'dashboard_performance_indicators', 'dashboard_charts', 'dashboard_chart_type', 'dashboard_chart_interval', diff --git a/includes/api/class-wc-admin-rest-reports-controller.php b/includes/api/class-wc-admin-rest-reports-controller.php index 34d07e27403..d0506bc04d1 100644 --- a/includes/api/class-wc-admin-rest-reports-controller.php +++ b/includes/api/class-wc-admin-rest-reports-controller.php @@ -163,7 +163,7 @@ public function get_items( $request ) { $url_slug = $report['slug']; } - $report['url'] = admin_url( 'admin.php?page=wc-admin#/' . $url_slug ); + $report['url'] = '/analytics/' . $url_slug; } $item = $this->prepare_item_for_response( (object) $report, $request ); diff --git a/includes/api/class-wc-admin-rest-reports-coupons-stats-controller.php b/includes/api/class-wc-admin-rest-reports-coupons-stats-controller.php index ee963f7eef3..93c428c5752 100644 --- a/includes/api/class-wc-admin-rest-reports-coupons-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-coupons-stats-controller.php @@ -138,6 +138,8 @@ public function get_item_schema() { 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'coupons_count' => array( 'description' => __( 'Amount of coupons.', 'wc-admin' ), @@ -146,10 +148,11 @@ public function get_item_schema() { 'readonly' => true, ), 'orders_count' => array( - 'description' => __( 'Amount of orders.', 'wc-admin' ), + 'description' => __( 'Amount of discounted orders.', 'wc-admin' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, ), ); diff --git a/includes/api/class-wc-admin-rest-reports-downloads-stats-controller.php b/includes/api/class-wc-admin-rest-reports-downloads-stats-controller.php index 8726c677f4f..e5757c437cf 100644 --- a/includes/api/class-wc-admin-rest-reports-downloads-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-downloads-stats-controller.php @@ -143,6 +143,7 @@ public function get_item_schema() { 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, ), ); diff --git a/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php b/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php index 82247536122..d0034d927ad 100644 --- a/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-orders-stats-controller.php @@ -146,18 +146,22 @@ public function get_item_schema() { 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, - ), - 'avg_order_value' => array( - 'description' => __( 'Average order value.', 'wc-admin' ), - 'type' => 'number', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, + 'format' => 'currency', ), 'orders_count' => array( 'description' => __( 'Amount of orders', 'wc-admin' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), '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( 'description' => __( 'Average items per order', 'wc-admin' ), diff --git a/includes/api/class-wc-admin-rest-reports-performance-indicators-controller.php b/includes/api/class-wc-admin-rest-reports-performance-indicators-controller.php index e9bf0f7de03..e8fcfab80db 100644 --- a/includes/api/class-wc-admin-rest-reports-performance-indicators-controller.php +++ b/includes/api/class-wc-admin-rest-reports-performance-indicators-controller.php @@ -59,6 +59,39 @@ class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Re */ 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. * @@ -79,8 +112,6 @@ protected function prepare_reports_query( $request ) { * @return WP_Error|True */ 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; @@ -90,7 +121,6 @@ private function get_indicator_data() { $response = rest_do_request( $request ); $endpoints = $response->get_data(); $allowed_stats = array(); - if ( 200 !== $response->get_status() ) { return new WP_Error( 'woocommerce_reports_performance_indicators_result_failed', __( 'Sorry, fetching performance indicators failed.', 'wc-admin' ) ); } @@ -108,10 +138,15 @@ private function get_indicator_data() { } foreach ( $data['schema']['properties']['totals']['properties'] as $property_key => $schema_info ) { + if ( empty( $schema_info['indicator'] ) || ! $schema_info['indicator'] ) { + continue; + } + $stat = $prefix . '/' . $property_key; $allowed_stats[] = $stat; - $this->labels[ $stat ] = $schema_info['description']; + $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']; @@ -123,6 +158,95 @@ private function get_indicator_data() { 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 ) { + /** + * Custom ordering for store performance indicators. + * + * @see https://github.com/woocommerce/wc-admin/issues/1282 + * @param array $indicators A list of ordered indicators. + */ + $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; + } + } + /** * Get all reports. * @@ -146,7 +270,7 @@ public function get_items( $request ) { $pieces = $this->get_stats_parts( $stat ); $report = $pieces[0]; - $field = $pieces[1]; + $chart = $pieces[1]; if ( ! in_array( $stat, $this->allowed_stats ) ) { continue; @@ -160,12 +284,15 @@ public function get_items( $request ) { $response = rest_do_request( $request ); $data = $response->get_data(); + $format = $this->formats[ $stat ]; $label = $this->labels[ $stat ]; - if ( 200 !== $response->get_status() || ! isset( $data['totals'][ $field ] ) ) { + if ( 200 !== $response->get_status() || ! isset( $data['totals'][ $chart ] ) ) { $stats[] = (object) array( 'stat' => $stat, + 'chart' => $chart, 'label' => $label, + 'format' => $format, 'value' => null, ); continue; @@ -173,11 +300,15 @@ public function get_items( $request ) { $stats[] = (object) array( 'stat' => $stat, + 'chart' => $chart, 'label' => $label, - 'value' => $data['totals'][ $field ], + 'format' => $format, + 'value' => $data['totals'][ $chart ], ); } + usort( $stats, array( $this, 'sort' ) ); + $objects = array(); foreach ( $stats as $stat ) { $data = $this->prepare_item_for_response( $stat, $request ); @@ -239,7 +370,7 @@ protected function prepare_links( $object ) { 'href' => rest_url( $this->endpoints[ $endpoint ] ), ), 'report' => array( - 'href' => ! empty( $url ) ? $url . '?chart=' . $stat : '', + 'href' => ! empty( $url ) ? $url : '', ), ); @@ -287,12 +418,25 @@ public function get_item_schema() { '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( 'description' => __( 'Value of the stat. Returns null if the stat does not exist or cannot be loaded.', 'wc-admin' ), 'type' => 'number', @@ -305,6 +449,18 @@ public function get_item_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. * diff --git a/includes/api/class-wc-admin-rest-reports-products-stats-controller.php b/includes/api/class-wc-admin-rest-reports-products-stats-controller.php index bbc998196d8..5e5c4d0477d 100644 --- a/includes/api/class-wc-admin-rest-reports-products-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-products-stats-controller.php @@ -152,12 +152,14 @@ public function get_item_schema() { 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, ), 'net_revenue' => array( 'description' => __( 'Net revenue.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'format' => 'currency', ), 'orders_count' => array( 'description' => __( 'Number of orders.', 'wc-admin' ), diff --git a/includes/api/class-wc-admin-rest-reports-revenue-stats-controller.php b/includes/api/class-wc-admin-rest-reports-revenue-stats-controller.php index b5d77586ab7..f03a45784bc 100644 --- a/includes/api/class-wc-admin-rest-reports-revenue-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-revenue-stats-controller.php @@ -136,51 +136,61 @@ public function get_item_schema() { 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'net_revenue' => array( 'description' => __( 'Net revenue.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'coupons' => array( 'description' => __( 'Total of coupons.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'format' => 'currency', ), 'shipping' => array( 'description' => __( 'Total of shipping.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'taxes' => array( 'description' => __( 'Total of taxes.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'format' => 'currency', ), 'refunds' => array( 'description' => __( 'Total of refunds.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'orders_count' => array( - 'description' => __( 'Amount of orders', 'wc-admin' ), + 'description' => __( 'Amount of orders.', 'wc-admin' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'num_items_sold' => array( - 'description' => __( 'Amount of orders', 'wc-admin' ), + 'description' => __( 'Items sold.', 'wc-admin' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'products' => array( - 'description' => __( 'Amount of orders', 'wc-admin' ), + 'description' => __( 'Products sold.', 'wc-admin' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, diff --git a/includes/api/class-wc-admin-rest-reports-taxes-stats-controller.php b/includes/api/class-wc-admin-rest-reports-taxes-stats-controller.php index 59b466b5339..0851f6055c4 100644 --- a/includes/api/class-wc-admin-rest-reports-taxes-stats-controller.php +++ b/includes/api/class-wc-admin-rest-reports-taxes-stats-controller.php @@ -167,18 +167,24 @@ public function get_item_schema() { 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'order_tax' => array( 'description' => __( 'Order tax.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'shipping_tax' => array( 'description' => __( 'Shipping tax.', 'wc-admin' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, + 'indicator' => true, + 'format' => 'currency', ), 'orders_count' => array( 'description' => __( 'Amount of orders.', 'wc-admin' ), diff --git a/lib/admin.php b/lib/admin.php index 78b3995e82a..bfc132c9295 100644 --- a/lib/admin.php +++ b/lib/admin.php @@ -399,6 +399,7 @@ function wc_admin_get_user_data_fields() { 'revenue_report_columns', 'taxes_report_columns', 'variations_report_columns', + 'dashboard_performance_indicators', 'dashboard_charts', 'dashboard_chart_type', 'dashboard_chart_interval', diff --git a/lib/client-assets.php b/lib/client-assets.php index 259496fd3e5..811cd09b9dc 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -153,7 +153,8 @@ function wc_admin_print_script_settings() { } $preload_data_endpoints = array( - 'countries' => '/wc/v3/data/countries', + 'countries' => '/wc/v3/data/countries', + 'performanceIndicators' => '/wc/v3/reports/performance-indicators/allowed', ); if ( function_exists( 'gutenberg_preload_api_request' ) ) { @@ -169,9 +170,11 @@ function wc_admin_print_script_settings() { $current_user_data = array(); 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 ) ); } + $current_user_data = wc_admin_get_user_defaults( $current_user_data ); + /** * TODO: On merge, once plugin images are added to core WooCommerce, `wcAdminAssetUrl` can be retired, and * `wcAssetUrl` can be used in its place throughout the codebase. @@ -179,22 +182,22 @@ function wc_admin_print_script_settings() { // Settings and variables can be passed here for access in the app. $settings = array( - 'adminUrl' => admin_url(), - 'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ), - 'wcAdminAssetUrl' => plugins_url( 'images/', wc_admin_dir_path( 'wc-admin.php' ) ), // Temporary for plugin. See above. - 'embedBreadcrumbs' => wc_admin_get_embed_breadcrumbs(), - 'siteLocale' => esc_attr( get_bloginfo( 'language' ) ), - 'currency' => wc_admin_currency_settings(), - 'orderStatuses' => format_order_statuses( wc_get_order_statuses() ), - 'stockStatuses' => wc_get_product_stock_status_options(), - 'siteTitle' => get_bloginfo( 'name' ), - 'trackingEnabled' => $tracking_enabled, - 'dataEndpoints' => array(), - 'l10n' => array( + 'adminUrl' => admin_url(), + 'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ), + 'wcAdminAssetUrl' => plugins_url( 'images/', wc_admin_dir_path( 'wc-admin.php' ) ), // Temporary for plugin. See above. + 'embedBreadcrumbs' => wc_admin_get_embed_breadcrumbs(), + 'siteLocale' => esc_attr( get_bloginfo( 'language' ) ), + 'currency' => wc_admin_currency_settings(), + 'orderStatuses' => format_order_statuses( wc_get_order_statuses() ), + 'stockStatuses' => wc_get_product_stock_status_options(), + 'siteTitle' => get_bloginfo( 'name' ), + 'trackingEnabled' => $tracking_enabled, + 'dataEndpoints' => array(), + 'l10n' => array( 'userLocale' => get_user_locale(), 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), ), - 'currentUserData' => $current_user_data, + 'currentUserData' => $current_user_data, ); foreach ( $preload_data_endpoints as $key => $endpoint ) { @@ -211,6 +214,27 @@ function wc_admin_print_script_settings() { } add_action( 'admin_print_footer_scripts', 'wc_admin_print_script_settings', 1 ); +/** + * Sets default values for user preferences. + * + * @param array $user_data Array of user data. + * @return array Filtered array of user data. + */ +function wc_admin_get_user_defaults( $user_data ) { + // If no settings for performance indicators are stored, these are the defaults to disable. + if ( ! is_array( $user_data['dashboard_performance_indicators'] ) ) { + $user_data['dashboard_performance_indicators'] = array( + 'coupons/orders_count', + 'taxes/total_tax', + 'taxes/order_tax', + 'taxes/shipping_tax', + 'downloads/download_count', + ); + } + + return $user_data; +} + /** * Load plugin text domain for translations. */ diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index cd68ee2d2e5..73cf9cc4c64 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -12,6 +12,7 @@ - Fix to avoid duplicated Y-axis ticks when the Y max value was 0. - Remove decimals from Y-axis when displaying currencies. - Fix date formatting on charts in Safari. +- Make `href`/linking optional in SummaryNumber. # 1.3.0 diff --git a/packages/components/src/summary/number.js b/packages/components/src/summary/number.js index 42dfa3020f1..dd4559ed454 100644 --- a/packages/components/src/summary/number.js +++ b/packages/components/src/summary/number.js @@ -52,17 +52,23 @@ const SummaryNumber = ( { screenReaderLabel = sprintf( __( 'No change from %s', 'wc-admin' ), prevLabel ); } - const Container = onToggle ? Button : Link; + let Container; const containerProps = { className: classes, 'aria-current': selected ? 'page' : null, }; - if ( ! onToggle ) { - containerProps.href = href; - containerProps.role = 'menuitem'; + + if ( onToggle || href ) { + Container = onToggle ? Button : Link; + if ( ! onToggle ) { + containerProps.href = href; + containerProps.role = 'menuitem'; + } else { + containerProps.onClick = onToggle; + containerProps[ 'aria-expanded' ] = isOpen; + } } else { - containerProps.onClick = onToggle; - containerProps[ 'aria-expanded' ] = isOpen; + Container = 'div'; } return ( @@ -109,7 +115,7 @@ SummaryNumber.propTypes = { /** * 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, * and only applies to the toggle-able item (first in the list). @@ -147,7 +153,7 @@ SummaryNumber.propTypes = { }; SummaryNumber.defaultProps = { - href: '/analytics', + href: '', isOpen: false, prevLabel: __( 'Previous Period:', 'wc-admin' ), reverseTrend: false, diff --git a/tests/api/reports-performance-indicators.php b/tests/api/reports-performance-indicators.php index 52699a48d92..075427baaa0 100644 --- a/tests/api/reports-performance-indicators.php +++ b/tests/api/reports-performance-indicators.php @@ -37,6 +37,7 @@ public function test_register_routes() { $routes = $this->server->get_routes(); $this->assertArrayHasKey( $this->endpoint, $routes ); + $this->assertArrayHasKey( $this->endpoint . '/allowed', $routes ); } /** @@ -101,12 +102,14 @@ public function test_get_indicators() { $this->assertEquals( 'orders/orders_count', $reports[0]['stat'] ); $this->assertEquals( 'Amount of orders', $reports[0]['label'] ); $this->assertEquals( 1, $reports[0]['value'] ); - $this->assertEquals( 'http://example.org/wp-admin/admin.php?page=wc-admin#/orders?chart=orders_count', $response->data[0]['_links']['report'][0]['href'] ); + $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( 'Number of downloads.', $reports[1]['label'] ); + $this->assertEquals( 'Number of downloads', $reports[1]['label'] ); $this->assertEquals( 2, $reports[1]['value'] ); - $this->assertEquals( 'http://example.org/wp-admin/admin.php?page=wc-admin#/downloads?chart=download_count', $response->data[1]['_links']['report'][0]['href'] ); + $this->assertEquals( 'download_count', $reports[1]['chart'] ); + $this->assertEquals( '/analytics/downloads', $response->data[1]['_links']['report'][0]['href'] ); } /** @@ -151,9 +154,11 @@ public function test_indicators_schema() { $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 3, count( $properties ) ); + $this->assertEquals( 5, count( $properties ) ); $this->assertArrayHasKey( 'stat', $properties ); + $this->assertArrayHasKey( 'chart', $properties ); $this->assertArrayHasKey( 'label', $properties ); + $this->assertArrayHasKey( 'format', $properties ); $this->assertArrayHasKey( 'value', $properties ); } }