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 { getReportChartData, getReportTableData } from 'store/reports/utils';
|
||||
import withSelect from 'wc-api/with-select';
|
||||
import { extendTableData } from './utils';
|
||||
|
||||
const TABLE_FILTER = 'woocommerce_admin_report_table';
|
||||
|
||||
|
@ -115,6 +116,16 @@ ReportTable.propTypes = {
|
|||
* The endpoint to use in API calls.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -162,10 +173,11 @@ export default compose(
|
|||
? getReportChartData( chartEndpoint, 'primary', query, select )
|
||||
: {};
|
||||
const queriedTableData = tableData || getReportTableData( endpoint, query, select, tableQuery );
|
||||
const extendedTableData = extendTableData( select, props, queriedTableData );
|
||||
|
||||
const selectProps = {
|
||||
primaryData,
|
||||
tableData: queriedTableData,
|
||||
tableData: extendedTableData,
|
||||
};
|
||||
|
||||
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' ),
|
||||
screenReaderLabel: __( 'Average Order Value', 'wc-admin' ),
|
||||
key: 'average_order_value',
|
||||
key: 'avg_order_value',
|
||||
isNumeric: true,
|
||||
},
|
||||
{
|
||||
|
@ -98,19 +98,20 @@ export default class CustomersReportTable extends Component {
|
|||
|
||||
return customers.map( customer => {
|
||||
const {
|
||||
average_order_value,
|
||||
id,
|
||||
city,
|
||||
country,
|
||||
avg_order_value,
|
||||
billing,
|
||||
date_last_active,
|
||||
date_sign_up,
|
||||
email,
|
||||
name,
|
||||
first_name,
|
||||
id,
|
||||
last_name,
|
||||
orders_count,
|
||||
postal_code,
|
||||
username,
|
||||
total_spend,
|
||||
} = customer;
|
||||
const { postcode, city, country } = billing || {};
|
||||
const name = `${ first_name } ${ last_name }`;
|
||||
|
||||
const customerNameLink = (
|
||||
<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 ),
|
||||
},
|
||||
{
|
||||
display: average_order_value,
|
||||
value: getCurrencyFormatDecimal( average_order_value ),
|
||||
display: formatCurrency( avg_order_value ),
|
||||
value: getCurrencyFormatDecimal( avg_order_value ),
|
||||
},
|
||||
{
|
||||
display: formatDate( formats.tableFormat, date_last_active ),
|
||||
|
@ -160,8 +161,8 @@ export default class CustomersReportTable extends Component {
|
|||
value: city,
|
||||
},
|
||||
{
|
||||
display: postal_code,
|
||||
value: postal_code,
|
||||
display: postcode,
|
||||
value: postcode,
|
||||
},
|
||||
];
|
||||
} );
|
||||
|
@ -174,6 +175,11 @@ export default class CustomersReportTable extends Component {
|
|||
<ReportTable
|
||||
compareBy="customers"
|
||||
endpoint="customers"
|
||||
extendItemsMethodNames={ {
|
||||
load: 'getCustomers',
|
||||
getError: 'getCustomersError',
|
||||
isRequesting: 'isGetCustomersRequesting',
|
||||
} }
|
||||
getHeadersContent={ this.getHeadersContent }
|
||||
getRowsContent={ this.getRowsContent }
|
||||
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
|
||||
*/
|
||||
import customers from './customers';
|
||||
import notes from './notes';
|
||||
import orders from './orders';
|
||||
import reportItems from './reports/items';
|
||||
|
@ -16,6 +17,7 @@ function createWcApiSpec() {
|
|||
...user.mutations,
|
||||
},
|
||||
selectors: {
|
||||
...customers.selectors,
|
||||
...notes.selectors,
|
||||
...orders.selectors,
|
||||
...reportItems.selectors,
|
||||
|
@ -26,6 +28,7 @@ function createWcApiSpec() {
|
|||
operations: {
|
||||
read( resourceNames ) {
|
||||
return [
|
||||
...customers.operations.read( resourceNames ),
|
||||
...notes.operations.read( resourceNames ),
|
||||
...orders.operations.read( resourceNames ),
|
||||
...reportItems.operations.read( resourceNames ),
|
||||
|
|
Loading…
Reference in New Issue