Onboarding: Add purchase products task list item (https://github.com/woocommerce/woocommerce-admin/pull/3472)

* Add an array of installed plugins to wcSettings

* Don't include already installed plugins in cart items

* Move cart modal component

* Add purchase task and modal to task list

* Rename ambiguous task getter method

* Remove modal purchase later prop

* Show completed product purchase task item after purchase

* Don't show cart modal if all items previously purchased
This commit is contained in:
Joshua T Flowers 2019-12-31 16:50:45 +08:00 committed by GitHub
parent 1a83c50e19
commit 700cf7bb2d
7 changed files with 83 additions and 23 deletions

View File

@ -24,7 +24,7 @@ import Section from './section';
import withSelect from 'wc-api/with-select';
import { recordEvent } from 'lib/tracks';
import TaskList from './task-list';
import { getTasks } from './task-list/tasks';
import { getAllTasks } from './task-list/tasks';
import { isOnboardingEnabled } from 'dashboard/utils';
import { getCurrentDates, getDateParamsFromQuery, isoDateFormat } from 'lib/date';
import ReportFilters from 'analytics/components/report-filters';
@ -277,7 +277,7 @@ export default compose(
if ( isOnboardingEnabled() ) {
const profileItems = getProfileItems();
const tasks = getTasks( {
const tasks = getAllTasks( {
profileItems,
options: getOptions( [ 'woocommerce_task_list_payments' ] ),
query: props.query,

View File

@ -17,7 +17,7 @@ import { updateQueryString } from '@woocommerce/navigation';
* Internal dependencies
*/
import BusinessDetails from './steps/business-details';
import CartModal from './cart-modal';
import CartModal from '../components/cart-modal';
import Industry from './steps/industry';
import Plugins from './steps/plugins';
import ProductTypes from './steps/product-types';

View File

@ -4,7 +4,7 @@
*/
import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { filter, get } from 'lodash';
import { get } from 'lodash';
import { compose } from '@wordpress/compose';
import classNames from 'classnames';
import { Snackbar, Icon, Button, Modal } from '@wordpress/components';
@ -20,14 +20,16 @@ import { updateQueryString } from '@woocommerce/navigation';
* Internal dependencies
*/
import './style.scss';
import withSelect from 'wc-api/with-select';
import CartModal from '../components/cart-modal';
import { getAllTasks } from './tasks';
import { recordEvent } from 'lib/tracks';
import { getTasks } from './tasks';
import withSelect from 'wc-api/with-select';
class TaskDashboard extends Component {
constructor( props ) {
super( props );
this.state = {
isCartModalOpen: false,
isWelcomeModalOpen: ! props.modalDismissed,
};
}
@ -61,6 +63,17 @@ class TaskDashboard extends Component {
document.body.classList.remove( 'woocommerce-task-dashboard__body' );
}
getTasks() {
const { profileItems, query, taskListPayments } = this.props;
return getAllTasks( {
profileItems,
options: taskListPayments,
query: query,
toggleCartModal: this.toggleCartModal.bind( this ),
} ).filter( task => task.visible );
}
recordTaskView() {
const { task } = this.props.query;
@ -78,7 +91,7 @@ class TaskDashboard extends Component {
return;
}
const { profileItems } = this.props;
const tasks = filter( this.props.tasks, task => task.visible );
const tasks = this.getTasks();
recordEvent( 'tasklist_view', {
number_tasks: tasks.length,
store_connected: profileItems.wccom_connected,
@ -107,7 +120,7 @@ class TaskDashboard extends Component {
getCurrentTask() {
const { task } = this.props.query;
const currentTask = this.props.tasks.find( s => s.key === task );
const currentTask = this.getTasks().find( s => s.key === task );
if ( ! currentTask ) {
return null;
@ -156,6 +169,10 @@ class TaskDashboard extends Component {
);
}
toggleCartModal() {
this.setState( { isCartModalOpen: ! this.state.isCartModalOpen } );
}
closeWelcomeModal() {
// Prevent firing this event before the modal is seen.
if ( document.body.classList.contains( 'woocommerce-admin-is-loading' ) ) {
@ -211,10 +228,10 @@ class TaskDashboard extends Component {
}
render() {
const { inline, tasks } = this.props;
const { isWelcomeModalOpen } = this.state;
const { inline } = this.props;
const { isCartModalOpen, isWelcomeModalOpen } = this.state;
const currentTask = this.getCurrentTask();
const listTasks = filter( tasks, task => task.visible ).map( task => {
const listTasks = this.getTasks().map( task => {
task.className = classNames( task.completed ? 'is-complete' : null, task.className );
task.before = task.completed ? (
<i className="material-icons-outlined">check_circle</i>
@ -253,13 +270,19 @@ class TaskDashboard extends Component {
</Fragment>
) }
</div>
{ isCartModalOpen && (
<CartModal
onClose={ () => this.toggleCartModal() }
onClickPurchaseLater={ () => this.toggleCartModal() }
/>
) }
</Fragment>
);
}
}
export default compose(
withSelect( ( select, props ) => {
withSelect( select => {
const { getProfileItems, getOptions } = select( 'wc-api' );
const profileItems = getProfileItems();
@ -273,18 +296,13 @@ export default compose(
[ 'woocommerce_task_list_welcome_modal_dismissed' ],
false
);
const tasks = getTasks( {
profileItems,
options: getOptions( [ 'woocommerce_task_list_payments' ] ),
query: props.query,
} );
const taskListPayments = getOptions( [ 'woocommerce_task_list_payments' ] );
return {
modalDismissed,
profileItems,
promptShown,
tasks,
taskListPayments,
};
} ),
withDispatch( dispatch => {

View File

@ -19,12 +19,13 @@ import { updateQueryString } from '@woocommerce/navigation';
*/
import Appearance from './tasks/appearance';
import Connect from './tasks/connect';
import { getProductIdsForCart } from 'dashboard/utils';
import Products from './tasks/products';
import Shipping from './tasks/shipping';
import Tax from './tasks/tax';
import Payments from './tasks/payments';
export function getTasks( { profileItems, options, query } ) {
export function getAllTasks( { profileItems, options, query, toggleCartModal } ) {
const {
hasHomepage,
hasPhysicalProducts,
@ -39,6 +40,9 @@ export function getTasks( { profileItems, options, query } ) {
shippingZonesCount: 0,
} );
const productIds = getProductIdsForCart( profileItems, true );
const remainingProductIds = getProductIdsForCart( profileItems );
const paymentsCompleted = get(
options,
[ 'woocommerce_task_list_payments', 'completed' ],
@ -46,6 +50,19 @@ export function getTasks( { profileItems, options, query } ) {
);
const tasks = [
{
key: 'purchase',
title: __( 'Purchase & install extensions', 'woocommerce-admin' ),
content: __(
'Purchase, install, and manage your extensions directly from your dashboard',
'wooocommerce-admin'
),
icon: 'extension',
container: null,
onClick: () => ( remainingProductIds.length ? toggleCartModal() : null ),
visible: productIds.length,
completed: ! remainingProductIds.length,
},
{
key: 'connect',
title: __( 'Connect your store to WooCommerce.com', 'woocommerce-admin' ),

View File

@ -41,9 +41,10 @@ export function getCurrencyRegion( countryState ) {
* Gets the product IDs for items based on the product types and theme selected in the onboarding profiler.
*
* @param {object} profileItems Onboarding profile.
* @param {bool} includeInstalledItems Include installed items in returned product IDs.
* @return {array} Product Ids.
*/
export function getProductIdsForCart( profileItems ) {
export function getProductIdsForCart( profileItems, includeInstalledItems = false ) {
const productIds = [];
const onboarding = getSetting( 'onboarding', {} );
const productTypes = profileItems.product_types || [];
@ -51,7 +52,9 @@ export function getProductIdsForCart( profileItems ) {
productTypes.forEach( productType => {
if (
onboarding.productTypes[ productType ] &&
onboarding.productTypes[ productType ].product
onboarding.productTypes[ productType ].product &&
( includeInstalledItems ||
! onboarding.installedPlugins.includes( onboarding.productTypes[ productType ].slug ) )
) {
productIds.push( onboarding.productTypes[ productType ].product );
}
@ -59,7 +62,12 @@ export function getProductIdsForCart( profileItems ) {
const theme = onboarding.themes.find( themeData => themeData.slug === profileItems.theme );
if ( theme && theme.id && ! theme.is_installed && getPriceValue( theme.price ) > 0 ) {
if (
theme &&
theme.id &&
getPriceValue( theme.price ) > 0 &&
( includeInstalledItems || ! theme.is_installed )
) {
productIds.push( theme.id );
}

View File

@ -326,6 +326,7 @@ class Onboarding {
$product_types[ $key ]['label'] .= sprintf( __( ' — %s per year', 'woocommerce-admin' ), html_entity_decode( $products[ $product_type['product'] ]->price ) );
$product_types[ $key ]['description'] = $products[ $product_type['product'] ]->excerpt;
$product_types[ $key ]['more_url'] = $products[ $product_type['product'] ]->link;
$product_types[ $key ]['slug'] = strtolower( preg_replace( '~[^\pL\d]+~u', '-', $products[ $product_type['product'] ]->slug ) );
} elseif ( isset( $product_type['product'] ) ) {
/* translators: site currency symbol (used to show that the product costs money) */
$product_types[ $key ]['label'] .= sprintf( __( ' — %s', 'woocommerce-admin' ), html_entity_decode( get_woocommerce_currency_symbol() ) );
@ -367,6 +368,7 @@ class Onboarding {
// Only fetch if the onboarding wizard OR the task list is incomplete.
if ( self::should_show_profiler() || self::should_show_tasks() ) {
$settings['onboarding']['activePlugins'] = self::get_active_plugins();
$settings['onboarding']['installedPlugins'] = self::get_installed_plugins();
$settings['onboarding']['stripeSupportedCountries'] = self::get_stripe_supported_countries();
$settings['onboarding']['euCountries'] = WC()->countries->get_european_union_countries();
$settings['onboarding']['connectNonce'] = wp_create_nonce( 'connect' );
@ -537,6 +539,21 @@ class Onboarding {
return apply_filters( 'woocommerce_admin_onboarding_themes_whitelist', $allowed_themes );
}
/**
* Get an array of installed plugin slugs.
*
* @return array
*/
public static function get_installed_plugins() {
return array_map(
function( $plugin_path ) {
$path_parts = explode( '/', $plugin_path );
return $path_parts[0];
},
array_keys( get_plugins() )
);
}
/**
* Let the app know that we will be showing the onboarding route, so wp-admin elements should be hidden while loading.
*