Remove updated stock products from Activity Panel (https://github.com/woocommerce/woocommerce-admin/pull/2442)
* Remove updated stock products from Activity Panel * Use prefers-reduced-motion preference * Update comparison to check if stock quantity is 'lower or equal to' lowStockAmount * Focus quantityInput on 'beginEdit' instead of 'componentDidUpdate' * Add comment explaining why we hide cards * Refactor updateProductStock action * Add type and parent_id properties to update
This commit is contained in:
parent
2f4b8272e6
commit
808143d7c9
|
@ -310,6 +310,14 @@
|
|||
}
|
||||
|
||||
.woocommerce-stock-activity-card {
|
||||
@media screen and (prefers-reduced-motion: no-preference) {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&.is-dimmed {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.woocommerce-stock-activity-card__stock-quantity {
|
||||
background: $core-grey-light-300;
|
||||
color: $core-grey-dark-500;
|
||||
|
|
|
@ -27,27 +27,36 @@ class ProductStockCard extends Component {
|
|||
this.state = {
|
||||
quantity: props.product.stock_quantity,
|
||||
editing: false,
|
||||
edited: false,
|
||||
};
|
||||
|
||||
this.beginEdit = this.beginEdit.bind( this );
|
||||
this.cancelEdit = this.cancelEdit.bind( this );
|
||||
this.onQuantityChange = this.onQuantityChange.bind( this );
|
||||
this.handleKeyDown = this.handleKeyDown.bind( this );
|
||||
this.updateStock = this.updateStock.bind( this );
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.quantityInput && this.quantityInput.focus();
|
||||
this.onSubmit = this.onSubmit.bind( this );
|
||||
}
|
||||
|
||||
beginEdit() {
|
||||
this.setState( { editing: true } );
|
||||
const { product } = this.props;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editing: true,
|
||||
quantity: product.stock_quantity,
|
||||
},
|
||||
() => {
|
||||
this.quantityInput && this.quantityInput.focus();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
cancelEdit() {
|
||||
const { product } = this.props;
|
||||
|
||||
this.setState( {
|
||||
editing: false,
|
||||
quantity: this.props.product.stock_quantity,
|
||||
quantity: product.stock_quantity,
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -61,18 +70,13 @@ class ProductStockCard extends Component {
|
|||
this.setState( { quantity: event.target.value } );
|
||||
}
|
||||
|
||||
updateStock() {
|
||||
const { product, updateItem } = this.props;
|
||||
onSubmit() {
|
||||
const { product, updateProductStock } = this.props;
|
||||
const { quantity } = this.state;
|
||||
|
||||
this.setState( { editing: false }, () => {
|
||||
const data = {
|
||||
stock_quantity: this.state.quantity,
|
||||
type: product.type,
|
||||
parent_id: product.parent_id,
|
||||
};
|
||||
this.setState( { editing: false, edited: true } );
|
||||
|
||||
updateItem( 'products', product.id, data );
|
||||
} );
|
||||
updateProductStock( product, quantity );
|
||||
}
|
||||
|
||||
getActions() {
|
||||
|
@ -95,6 +99,7 @@ class ProductStockCard extends Component {
|
|||
}
|
||||
|
||||
getBody() {
|
||||
const { product } = this.props;
|
||||
const { editing, quantity } = this.state;
|
||||
|
||||
if ( editing ) {
|
||||
|
@ -119,14 +124,27 @@ class ProductStockCard extends Component {
|
|||
|
||||
return (
|
||||
<span className="woocommerce-stock-activity-card__stock-quantity">
|
||||
{ sprintf( __( '%d in stock', 'woocommerce-admin' ), quantity ) }
|
||||
{ sprintf( __( '%d in stock', 'woocommerce-admin' ), product.stock_quantity ) }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { product } = this.props;
|
||||
const { editing } = this.state;
|
||||
const { edited, editing } = this.state;
|
||||
const { notifyLowStockAmount } = wcSettings;
|
||||
const lowStockAmount = Number.isFinite( product.low_stock_amount )
|
||||
? product.low_stock_amount
|
||||
: notifyLowStockAmount;
|
||||
const isLowStock = product.stock_quantity <= lowStockAmount;
|
||||
|
||||
// Hide cards that are not in low stock and have not been edited.
|
||||
// This allows clearing cards which are no longer in low stock after
|
||||
// closing & opening the panel without having to make another request.
|
||||
if ( ! isLowStock && ! edited ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = (
|
||||
<Link
|
||||
href={ 'post.php?action=edit&post=' + ( product.parent_id || product.id ) }
|
||||
|
@ -157,10 +175,13 @@ class ProductStockCard extends Component {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
const activityCardClasses = classnames( 'woocommerce-stock-activity-card', {
|
||||
'is-dimmed': ! editing && ! isLowStock,
|
||||
} );
|
||||
|
||||
const activityCard = (
|
||||
<ActivityCard
|
||||
className="woocommerce-stock-activity-card"
|
||||
className={ activityCardClasses }
|
||||
title={ title }
|
||||
subtitle={ subtitle }
|
||||
icon={ icon }
|
||||
|
@ -172,7 +193,7 @@ class ProductStockCard extends Component {
|
|||
|
||||
if ( editing ) {
|
||||
return (
|
||||
<form onReset={ this.cancelEdit } onSubmit={ this.updateStock }>
|
||||
<form onReset={ this.cancelEdit } onSubmit={ this.onSubmit }>
|
||||
{ activityCard }
|
||||
</form>
|
||||
);
|
||||
|
@ -184,9 +205,10 @@ class ProductStockCard extends Component {
|
|||
|
||||
export default compose(
|
||||
withDispatch( dispatch => {
|
||||
const { updateItem } = dispatch( 'wc-api' );
|
||||
const { updateProductStock } = dispatch( 'wc-api' );
|
||||
|
||||
return {
|
||||
updateItem,
|
||||
updateProductStock,
|
||||
};
|
||||
} )
|
||||
)( ProductStockCard );
|
||||
|
|
|
@ -1,15 +1,52 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getResourceName } from '../utils';
|
||||
|
||||
const updateItem = operations => ( type, id, itemData ) => {
|
||||
const resourceName = getResourceName( `items-query-${ type }-item`, id );
|
||||
operations.update( [ resourceName ], { [ resourceName ]: { id, ...itemData } } );
|
||||
const updateProductStock = operations => async ( product, newStock ) => {
|
||||
const { addNotice } = dispatch( 'wc-admin' );
|
||||
const oldStockQuantity = product.stock_quantity;
|
||||
const resourceName = getResourceName( 'items-query-products-item', product.id );
|
||||
|
||||
// Optimistically update product stock
|
||||
operations.updateLocally( [ resourceName ], {
|
||||
[ resourceName ]: { ...product, stock_quantity: newStock },
|
||||
} );
|
||||
|
||||
const result = await operations.update( [ resourceName ], {
|
||||
[ resourceName ]: {
|
||||
id: product.id,
|
||||
type: product.type,
|
||||
parent_id: product.parent_id,
|
||||
stock_quantity: newStock,
|
||||
},
|
||||
} );
|
||||
const response = result[ 0 ][ resourceName ];
|
||||
if ( response && response.data ) {
|
||||
addNotice( {
|
||||
status: 'success',
|
||||
message: sprintf( __( '%s stock updated.', 'woocommerce-admin' ), product.name ),
|
||||
} );
|
||||
}
|
||||
if ( response && response.error ) {
|
||||
addNotice( {
|
||||
status: 'error',
|
||||
message: sprintf( __( '%s stock could not be updated.', 'woocommerce-admin' ), product.name ),
|
||||
} );
|
||||
// Revert local changes if the operation failed in the server
|
||||
operations.updateLocally( [ resourceName ], {
|
||||
[ resourceName ]: { ...product, stock_quantity: oldStockQuantity },
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
updateItem,
|
||||
updateProductStock,
|
||||
};
|
||||
|
|
|
@ -115,7 +115,19 @@ function update( resourceNames, data, fetch = apiFetch ) {
|
|||
} );
|
||||
}
|
||||
|
||||
function updateLocally( resourceNames, data ) {
|
||||
const updateableTypes = [ 'items-query-products-item' ];
|
||||
const filteredNames = resourceNames.filter( name => {
|
||||
return updateableTypes.includes( getResourcePrefix( name ) );
|
||||
} );
|
||||
|
||||
return filteredNames.map( async resourceName => {
|
||||
return { [ resourceName ]: { data: data[ resourceName ] } };
|
||||
} );
|
||||
}
|
||||
|
||||
export default {
|
||||
read,
|
||||
update,
|
||||
updateLocally,
|
||||
};
|
||||
|
|
|
@ -62,6 +62,9 @@ function createWcApiSpec() {
|
|||
...user.operations.update( resourceNames, data ),
|
||||
];
|
||||
},
|
||||
updateLocally( resourceNames, data ) {
|
||||
return [ ...items.operations.updateLocally( resourceNames, data ) ];
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -99,16 +99,46 @@ class WC_Admin_REST_Products_Controller extends WC_REST_Products_Controller {
|
|||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
add_filter( 'posts_fields', array( __CLASS__, 'add_wp_query_fields' ), 10, 2 );
|
||||
add_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10, 2 );
|
||||
add_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10, 2 );
|
||||
add_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10, 2 );
|
||||
$response = parent::get_items( $request );
|
||||
remove_filter( 'posts_fields', array( __CLASS__, 'add_wp_query_fields' ), 10 );
|
||||
remove_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10 );
|
||||
remove_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10 );
|
||||
remove_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10 );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add `low_stock_amount` property to product data
|
||||
*
|
||||
* @param WC_Data $object Object data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function prepare_object_for_response( $object, $request ) {
|
||||
$data = parent::prepare_object_for_response( $object, $request );
|
||||
if ( $request->get_param( 'low_in_stock' ) && is_numeric( $object->low_stock_amount ) ) {
|
||||
$data->data['low_stock_amount'] = $object->low_stock_amount;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add in conditional select fields to the query.
|
||||
*
|
||||
* @param string $select Select clause used to select fields from the query.
|
||||
* @param object $wp_query WP_Query object.
|
||||
* @return string
|
||||
*/
|
||||
public static function add_wp_query_fields( $select, $wp_query ) {
|
||||
if ( $wp_query->get( 'low_in_stock' ) ) {
|
||||
return $select . ', low_stock_amount_meta.meta_value AS low_stock_amount';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add in conditional search filters for products.
|
||||
*
|
||||
|
|
|
@ -519,12 +519,13 @@ class WC_Admin_Loader {
|
|||
$current_user_data[ $user_field ] = json_decode( get_user_meta( get_current_user_id(), 'wc_admin_' . $user_field, true ) );
|
||||
}
|
||||
|
||||
$settings['orderStatuses'] = self::get_order_statuses( wc_get_order_statuses() );
|
||||
$settings['currentUserData'] = $current_user_data;
|
||||
$settings['currency'] = self::get_currency_settings();
|
||||
$settings['reviewsEnabled'] = get_option( 'woocommerce_enable_reviews' );
|
||||
$settings['manageStock'] = get_option( 'woocommerce_manage_stock' );
|
||||
$settings['commentModeration'] = get_option( 'comment_moderation' );
|
||||
$settings['orderStatuses'] = self::get_order_statuses( wc_get_order_statuses() );
|
||||
$settings['currentUserData'] = $current_user_data;
|
||||
$settings['currency'] = self::get_currency_settings();
|
||||
$settings['reviewsEnabled'] = get_option( 'woocommerce_enable_reviews' );
|
||||
$settings['manageStock'] = get_option( 'woocommerce_manage_stock' );
|
||||
$settings['commentModeration'] = get_option( 'comment_moderation' );
|
||||
$settings['notifyLowStockAmount'] = get_option( 'woocommerce_notify_low_stock_amount' );
|
||||
// @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.
|
||||
$settings['wcAdminAssetUrl'] = plugins_url( 'images/', plugin_dir_path( dirname( __FILE__ ) ) . 'woocommerce-admin.php' );
|
||||
|
|
Loading…
Reference in New Issue