Merge pull request woocommerce/woocommerce-admin#1075 from woocommerce/add/extend-customers-data-from-endpoint

Extend customers table data with data from WC endpoint
This commit is contained in:
Albert Juhé Lluveras 2018-12-18 09:30:03 +01:00 committed by GitHub
commit 9ef07a41b6
7 changed files with 188 additions and 12 deletions

View File

@ -21,6 +21,7 @@ import { onQueryChange } from '@woocommerce/navigation';
import ReportError from 'analytics/components/report-error'; import ReportError from 'analytics/components/report-error';
import { getReportChartData, getReportTableData } from 'store/reports/utils'; import { getReportChartData, getReportTableData } from 'store/reports/utils';
import withSelect from 'wc-api/with-select'; import withSelect from 'wc-api/with-select';
import { extendTableData } from './utils';
const TABLE_FILTER = 'woocommerce_admin_report_table'; const TABLE_FILTER = 'woocommerce_admin_report_table';
@ -115,6 +116,16 @@ ReportTable.propTypes = {
* The endpoint to use in API calls. * The endpoint to use in API calls.
*/ */
endpoint: PropTypes.string, endpoint: PropTypes.string,
/**
* Name of the methods available via `select( 'wc-api' )` that will be used to
* load more data for table items. If omitted, no call will be made and only
* the data returned by the reports endpoint will be used.
*/
extendItemsMethodNames: PropTypes.shape( {
getError: PropTypes.string,
isRequesting: PropTypes.string,
load: PropTypes.string,
} ),
/** /**
* A function that returns the headers object to build the table. * A function that returns the headers object to build the table.
*/ */
@ -162,10 +173,11 @@ export default compose(
? getReportChartData( chartEndpoint, 'primary', query, select ) ? getReportChartData( chartEndpoint, 'primary', query, select )
: {}; : {};
const queriedTableData = tableData || getReportTableData( endpoint, query, select, tableQuery ); const queriedTableData = tableData || getReportTableData( endpoint, query, select, tableQuery );
const extendedTableData = extendTableData( select, props, queriedTableData );
const selectProps = { const selectProps = {
primaryData, primaryData,
tableData: queriedTableData, tableData: extendedTableData,
}; };
if ( columnPrefsKey ) { if ( columnPrefsKey ) {

View File

@ -0,0 +1,54 @@
/** @format */
/**
* External dependencies
*/
import { first } from 'lodash';
export function extendTableData( select, props, queriedTableData ) {
const { extendItemsMethodNames, itemIdField } = props;
const itemsData = queriedTableData.items.data;
if (
! Array.isArray( itemsData ) ||
! itemsData.length ||
! extendItemsMethodNames ||
! itemIdField
) {
return queriedTableData;
}
const {
[ extendItemsMethodNames.getError ]: getErrorMethod,
[ extendItemsMethodNames.isRequesting ]: isRequestingMethod,
[ extendItemsMethodNames.load ]: loadMethod,
} = select( 'wc-api' );
const extendQuery = {
include: itemsData.map( item => item[ itemIdField ] ).join( ',' ),
per_page: itemsData.length,
};
const extendedItems = loadMethod( extendQuery );
const isExtendedItemsRequesting = isRequestingMethod ? isRequestingMethod( extendQuery ) : false;
const isExtendedItemsError = getErrorMethod ? getErrorMethod( extendQuery ) : false;
const extendedItemsData = itemsData.map( item => {
const extendedItemData = first(
extendedItems.filter( extendedItem => item.id === extendedItem.id )
);
return {
...item,
...extendedItemData,
};
} );
const isRequesting = queriedTableData.isRequesting || isExtendedItemsRequesting;
const isError = queriedTableData.isError || isExtendedItemsError;
return {
...queriedTableData,
isRequesting,
isError,
items: {
...queriedTableData.items,
data: extendedItemsData,
},
};
}

View File

@ -66,7 +66,7 @@ export default class CustomersReportTable extends Component {
{ {
label: __( 'AOV', 'wc-admin' ), label: __( 'AOV', 'wc-admin' ),
screenReaderLabel: __( 'Average Order Value', 'wc-admin' ), screenReaderLabel: __( 'Average Order Value', 'wc-admin' ),
key: 'average_order_value', key: 'avg_order_value',
isNumeric: true, isNumeric: true,
}, },
{ {
@ -98,19 +98,20 @@ export default class CustomersReportTable extends Component {
return customers.map( customer => { return customers.map( customer => {
const { const {
average_order_value, avg_order_value,
id, billing,
city,
country,
date_last_active, date_last_active,
date_sign_up, date_sign_up,
email, email,
name, first_name,
id,
last_name,
orders_count, orders_count,
postal_code,
username, username,
total_spend, total_spend,
} = customer; } = customer;
const { postcode, city, country } = billing || {};
const name = `${ first_name } ${ last_name }`;
const customerNameLink = ( const customerNameLink = (
<Link href={ 'user-edit.php?user_id=' + id } type="wp-admin"> <Link href={ 'user-edit.php?user_id=' + id } type="wp-admin">
@ -144,8 +145,8 @@ export default class CustomersReportTable extends Component {
value: getCurrencyFormatDecimal( total_spend ), value: getCurrencyFormatDecimal( total_spend ),
}, },
{ {
display: average_order_value, display: formatCurrency( avg_order_value ),
value: getCurrencyFormatDecimal( average_order_value ), value: getCurrencyFormatDecimal( avg_order_value ),
}, },
{ {
display: formatDate( formats.tableFormat, date_last_active ), display: formatDate( formats.tableFormat, date_last_active ),
@ -160,8 +161,8 @@ export default class CustomersReportTable extends Component {
value: city, value: city,
}, },
{ {
display: postal_code, display: postcode,
value: postal_code, value: postcode,
}, },
]; ];
} ); } );
@ -174,6 +175,11 @@ export default class CustomersReportTable extends Component {
<ReportTable <ReportTable
compareBy="customers" compareBy="customers"
endpoint="customers" endpoint="customers"
extendItemsMethodNames={ {
load: 'getCustomers',
getError: 'getCustomersError',
isRequesting: 'isGetCustomersRequesting',
} }
getHeadersContent={ this.getHeadersContent } getHeadersContent={ this.getHeadersContent }
getRowsContent={ this.getRowsContent } getRowsContent={ this.getRowsContent }
itemIdField="id" itemIdField="id"

View File

@ -0,0 +1,11 @@
/** @format */
/**
* Internal dependencies
*/
import operations from './operations';
import selectors from './selectors';
export default {
operations,
selectors,
};

View File

@ -0,0 +1,47 @@
/** @format */
/**
* External dependencies
*/
import apiFetch from '@wordpress/api-fetch';
/**
* WooCommerce dependencies
*/
import { stringifyQuery } from '@woocommerce/navigation';
/**
* Internal dependencies
*/
import { isResourcePrefix, getResourceIdentifier, getResourceName } from '../utils';
import { NAMESPACE } from '../constants';
function read( resourceNames, fetch = apiFetch ) {
const filteredNames = resourceNames.filter( name => isResourcePrefix( name, 'customers-query' ) );
return filteredNames.map( async resourceName => {
const query = getResourceIdentifier( resourceName );
const url = `${ NAMESPACE }/customers${ stringifyQuery( query ) }`;
try {
const customers = await fetch( { path: url } );
const ids = customers.map( customer => customer.id );
const customerResources = customers.reduce( ( resources, customer ) => {
resources[ getResourceName( 'customer', customer.id ) ] = { data: customer };
return resources;
}, {} );
return {
[ resourceName ]: {
data: ids,
},
...customerResources,
};
} catch ( error ) {
return { [ resourceName ]: { error } };
}
} );
}
export default {
read,
};

View File

@ -0,0 +1,43 @@
/** @format */
/**
* External dependencies
*/
import { isNil } from 'lodash';
/**
* Internal dependencies
*/
import { getResourceName } from '../utils';
import { DEFAULT_REQUIREMENT } from '../constants';
const getCustomers = ( getResource, requireResource ) => (
query = {},
requirement = DEFAULT_REQUIREMENT
) => {
const resourceName = getResourceName( 'customers-query', query );
const ids = requireResource( requirement, resourceName ).data || [];
return ids.map( id => getResource( getResourceName( 'customer', id ) ).data || {} );
};
const getCustomersError = getResource => ( query = {} ) => {
const resourceName = getResourceName( 'customers-query', query );
return getResource( resourceName ).error;
};
const isGetCustomersRequesting = getResource => ( query = {} ) => {
const resourceName = getResourceName( 'customers-query', query );
const { lastRequested, lastReceived } = getResource( resourceName );
if ( isNil( lastRequested ) || isNil( lastReceived ) ) {
return true;
}
return lastRequested > lastReceived;
};
export default {
getCustomers,
getCustomersError,
isGetCustomersRequesting,
};

View File

@ -3,6 +3,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
import customers from './customers';
import notes from './notes'; import notes from './notes';
import orders from './orders'; import orders from './orders';
import reportItems from './reports/items'; import reportItems from './reports/items';
@ -16,6 +17,7 @@ function createWcApiSpec() {
...user.mutations, ...user.mutations,
}, },
selectors: { selectors: {
...customers.selectors,
...notes.selectors, ...notes.selectors,
...orders.selectors, ...orders.selectors,
...reportItems.selectors, ...reportItems.selectors,
@ -26,6 +28,7 @@ function createWcApiSpec() {
operations: { operations: {
read( resourceNames ) { read( resourceNames ) {
return [ return [
...customers.operations.read( resourceNames ),
...notes.operations.read( resourceNames ), ...notes.operations.read( resourceNames ),
...orders.operations.read( resourceNames ), ...orders.operations.read( resourceNames ),
...reportItems.operations.read( resourceNames ), ...reportItems.operations.read( resourceNames ),