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:
commit
9ef07a41b6
|
@ -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 ) {
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import operations from './operations';
|
||||||
|
import selectors from './selectors';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
operations,
|
||||||
|
selectors,
|
||||||
|
};
|
|
@ -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,
|
||||||
|
};
|
|
@ -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,
|
||||||
|
};
|
|
@ -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 ),
|
||||||
|
|
Loading…
Reference in New Issue