Onboarding: Update screen order and remove Jetpack connection in profiler (https://github.com/woocommerce/woocommerce-admin/pull/3739)

* Show usage modal every time on store details step

* Move plugin steps to end of profiler

* Rename Start step to Benefits

* Remove usage modal from benefits step

* Only show plugins step when plugins need to be installed

* Go to next step after profile item plugin data is updated

* Rename skip and start methods in benefits

* Add busy state to buttons

* Fix pending state on completion

* Cache active plugins to prevent removing active steps

* Use goToNextStep to complete plugin step and show cart modal

* Fix purchase_install event prop in cart modal

* Remove Jetpack connect step from plugins step of profiler

* Update welcome_clicked event name and props

* Remove cart modal from profiler

* Update benefits to show based on jetpack activation instead of connection

* Fix empty plugin data in profile items

* Only track previously installed plugins in componentDidMount

* Update benefits heading copy

* Use constants for checking Jetpack and WCS status
This commit is contained in:
Joshua T Flowers 2020-03-03 10:44:26 +01:00 committed by GitHub
parent 2046801e20
commit 8fa301d4b7
11 changed files with 108 additions and 242 deletions

View File

@ -37,13 +37,12 @@ class CartModal extends Component {
const { productIds, onClickPurchaseNow } = this.props;
this.setState( { purchaseNowButtonBusy: true } );
if ( ! productIds.length ) {
return;
}
recordEvent( 'tasklist_modal_proceed_checkout', {
product_ids: productIds,
purchase_install: false,
purchase_install: true,
} );
const { connectNonce } = getSetting( 'onboarding', {} );

View File

@ -10,33 +10,34 @@ import { withDispatch } from '@wordpress/data';
/**
* WooCommerce dependencies
*/
import { getSetting } from '@woocommerce/wc-admin-settings';
import { updateQueryString } from '@woocommerce/navigation';
/**
* Internal dependencies
*/
import Benefits from './steps/benefits';
import BusinessDetails from './steps/business-details';
import CartModal from '../components/cart-modal';
import Industry from './steps/industry';
import Plugins from './steps/plugins';
import ProductTypes from './steps/product-types';
import ProfileWizardHeader from './header';
import { QUERY_DEFAULTS } from 'wc-api/constants';
import { recordEvent } from 'lib/tracks';
import Start from './steps/start';
import StoreDetails from './steps/store-details';
import Theme from './steps/theme';
import withSelect from 'wc-api/with-select';
import { getProductIdsForCart } from 'dashboard/utils';
import './style.scss';
class ProfileWizard extends Component {
constructor() {
super( ...arguments );
this.state = {
showCartModal: false,
cartRedirectUrl: null,
};
const { activePlugins = [] } = getSetting( 'onboarding', {} );
this.activePlugins = activePlugins;
this.goToNextStep = this.goToNextStep.bind( this );
}
@ -67,10 +68,26 @@ class ProfileWizard extends Component {
}
componentDidMount() {
const { profileItems, updateProfileItems } = this.props;
document.documentElement.classList.remove( 'wp-toolbar' );
document.body.classList.add( 'woocommerce-onboarding' );
document.body.classList.add( 'woocommerce-profile-wizard__body' );
document.body.classList.add( 'woocommerce-admin-full-screen' );
// Track plugins if already installed.
if (
this.activePlugins.includes( 'woocommerce-services' ) &&
this.activePlugins.includes( 'jetpack' ) &&
profileItems.plugins !== 'already-installed'
) {
recordEvent(
'wcadmin_storeprofiler_already_installed_plugins',
{}
);
updateProfileItems( { plugins: 'already-installed' } );
}
}
componentWillUnmount() {
@ -91,17 +108,6 @@ class ProfileWizard extends Component {
const { profileItems } = this.props;
const steps = [];
steps.push( {
key: 'start',
container: Start,
} );
steps.push( {
key: 'plugins',
container: Plugins,
isComplete:
profileItems.hasOwnProperty( 'plugins' ) &&
profileItems.plugins !== null,
} );
steps.push( {
key: 'store-details',
container: StoreDetails,
@ -142,6 +148,27 @@ class ProfileWizard extends Component {
profileItems.hasOwnProperty( 'theme' ) &&
profileItems.theme !== null,
} );
if (
! this.activePlugins.includes( 'woocommerce-services' ) ||
! this.activePlugins.includes( 'jetpack' )
) {
steps.push( {
key: 'benefits',
container: Benefits,
} );
if (
profileItems.hasOwnProperty( 'plugins' ) &&
profileItems.plugins !== null &&
profileItems.plugins.startsWith( 'installed' )
) {
steps.push( {
key: 'plugins',
container: Plugins,
} );
}
}
return steps;
}
@ -167,28 +194,14 @@ class ProfileWizard extends Component {
} );
const nextStep = this.getSteps()[ currentStepIndex + 1 ];
if ( typeof nextStep === 'undefined' ) {
this.possiblyShowCart();
this.completeProfiler();
return;
}
return updateQueryString( { step: nextStep.key } );
}
possiblyShowCart() {
const { profileItems } = this.props;
// @todo This should also send profile information to woocommerce.com.
const productIds = getProductIdsForCart( profileItems );
if ( productIds.length ) {
this.setState( { showCartModal: true } );
} else {
this.completeProfiler();
}
}
completeProfiler() {
const { notes, updateNote, updateProfileItems } = this.props;
updateProfileItems( { completed: true } );
@ -202,14 +215,8 @@ class ProfileWizard extends Component {
}
}
markCompleteAndPurchase( cartRedirectUrl ) {
this.setState( { cartRedirectUrl } );
this.completeProfiler();
}
render() {
const { query } = this.props;
const { showCartModal } = this.state;
const step = this.getCurrentStep();
const container = createElement( step.container, {
@ -223,17 +230,6 @@ class ProfileWizard extends Component {
return (
<Fragment>
{ showCartModal && (
<CartModal
onClose={ () =>
this.setState( { showCartModal: false } )
}
onClickPurchaseNow={ ( cartRedirectUrl ) =>
this.markCompleteAndPurchase( cartRedirectUrl )
}
onClickPurchaseLater={ () => this.completeProfiler() }
/>
) }
<ProfileWizardHeader currentStep={ step.key } steps={ steps } />
<div className="woocommerce-profile-wizard__container">
{ container }

View File

@ -13,7 +13,6 @@ import { filter, get } from 'lodash';
* WooCommerce dependencies
*/
import { Card, H, Link } from '@woocommerce/components';
import { updateQueryString } from '@woocommerce/navigation';
/**
* Internal dependencies
@ -25,55 +24,40 @@ import SpeedIcon from './images/flash_on';
import MobileAppIcon from './images/phone_android';
import PrintIcon from './images/print';
import withSelect from 'wc-api/with-select';
import UsageModal from '../usage-modal';
import { recordEvent } from 'lib/tracks';
import { pluginNames } from 'wc-api/onboarding/constants';
class Start extends Component {
class Benefits extends Component {
constructor( props ) {
super( props );
this.state = {
showUsageModal: false,
continueAction: '',
isPending: false,
};
this.startWizard = this.startWizard.bind( this );
this.skipWizard = this.skipWizard.bind( this );
this.startPluginInstall = this.startPluginInstall.bind( this );
this.skipPluginInstall = this.skipPluginInstall.bind( this );
}
componentDidMount() {
const {
updateProfileItems,
profileItems,
tosAccepted,
isJetpackConnected,
} = this.props;
if (
isJetpackConnected &&
this.props.activePlugins.includes( 'woocommerce-services' ) &&
tosAccepted
) {
// Don't track event again if they revisit the start page.
if ( profileItems.plugins !== 'already-installed' ) {
recordEvent(
'wcadmin_storeprofiler_already_installed_plugins',
{}
);
}
componentDidUpdate( prevProps ) {
const { goToNextStep, isRequesting } = this.props;
const { isPending } = this.state;
updateProfileItems( { plugins: 'already-installed' } );
return updateQueryString( { step: 'store-details' } );
if ( ! isRequesting && prevProps.isRequesting && isPending ) {
goToNextStep();
this.setState( { isPending: false } );
}
}
async skipWizard() {
async skipPluginInstall() {
const {
createNotice,
isJetpackActive,
isProfileItemsError,
updateProfileItems,
isJetpackConnected,
} = this.props;
const plugins = isJetpackConnected ? 'skipped-wcs' : 'skipped';
this.setState( { isPending: true } );
const plugins = isJetpackActive ? 'skipped-wcs' : 'skipped';
await updateProfileItems( { plugins } );
if ( isProfileItemsError ) {
@ -85,46 +69,32 @@ class Start extends Component {
)
);
} else {
recordEvent( 'storeprofiler_welcome_clicked', {
get_started: true,
recordEvent( 'storeprofiler_install_plugins', {
install: false,
plugins,
} );
return updateQueryString( { step: 'store-details' } );
}
}
async startWizard() {
async startPluginInstall() {
const {
createNotice,
isProfileItemsError,
isJetpackActive,
updateProfileItems,
updateOptions,
goToNextStep,
isJetpackConnected,
} = this.props;
this.setState( { isPending: true } );
await updateOptions( {
woocommerce_setup_jetpack_opted_in: true,
} );
const plugins = isJetpackConnected ? 'installed-wcs' : 'installed';
await updateProfileItems( { plugins } );
if ( ! isProfileItemsError ) {
recordEvent( 'storeprofiler_welcome_clicked', {
get_started: true,
plugins,
} );
goToNextStep();
} else {
createNotice(
'error',
__(
'There was a problem updating your preferences.',
'woocommerce-admin'
)
);
}
const plugins = isJetpackActive ? 'installed-wcs' : 'installed';
recordEvent( 'storeprofiler_install_plugins', {
install: true,
plugins,
} );
updateProfileItems( { plugins } );
}
renderBenefit( benefit ) {
@ -144,7 +114,7 @@ class Start extends Component {
}
getBenefits() {
const { activePlugins, isJetpackConnected, tosAccepted } = this.props;
const { isJetpackActive, isWcsActive, tosAccepted } = this.props;
return [
{
title: __( 'Security', 'woocommerce-admin' ),
@ -153,7 +123,7 @@ class Start extends Component {
'Jetpack automatically blocks brute force attacks to protect your store from unauthorized access.',
'woocommerce-admin'
),
visible: ! isJetpackConnected,
visible: ! isJetpackActive,
},
{
title: __( 'Sales Tax', 'woocommerce-admin' ),
@ -162,9 +132,7 @@ class Start extends Component {
'With WooCommerce Services we ensure that the correct rate of tax is charged on all of your orders.',
'woocommerce-admin'
),
visible:
! activePlugins.includes( 'woocommerce-services' ) ||
! tosAccepted,
visible: ! isWcsActive || ! tosAccepted,
},
{
title: __( 'Speed', 'woocommerce-admin' ),
@ -173,7 +141,7 @@ class Start extends Component {
'Cache your images and static files on our own powerful global network of servers and speed up your site.',
'woocommerce-admin'
),
visible: ! isJetpackConnected,
visible: ! isJetpackActive,
},
{
title: __( 'Mobile App', 'woocommerce-admin' ),
@ -182,7 +150,7 @@ class Start extends Component {
'Your store in your pocket. Manage orders, receive sales notifications, and more. Only with a Jetpack connection.',
'woocommerce-admin'
),
visible: ! isJetpackConnected,
visible: ! isJetpackActive,
},
{
title: __(
@ -194,7 +162,7 @@ class Start extends Component {
'Save time at the Post Office by printing USPS shipping labels at home.',
'woocommerce-admin'
),
visible: isJetpackConnected || ! tosAccepted,
visible: isJetpackActive || ! tosAccepted,
},
{
title: __( 'Simple payment setup', 'woocommerce-admin' ),
@ -203,7 +171,7 @@ class Start extends Component {
'WooCommerce Services enables us to provision Stripe and Paypal accounts quickly and easily for you.',
'woocommerce-admin'
),
visible: isJetpackConnected || ! tosAccepted,
visible: isJetpackActive || ! tosAccepted,
},
];
}
@ -220,14 +188,14 @@ class Start extends Component {
}
render() {
const { showUsageModal, continueAction } = this.state;
const { isJetpackConnected, activePlugins } = this.props;
const { isJetpackActive, isWcsActive } = this.props;
const { isPending } = this.state;
const pluginsToInstall = [];
if ( ! isJetpackConnected ) {
if ( ! isJetpackActive ) {
pluginsToInstall.push( 'jetpack' );
}
if ( ! activePlugins.includes( 'woocommerce-services' ) ) {
if ( ! isWcsActive ) {
pluginsToInstall.push( 'woocommerce-services' );
}
const pluginNamesString = pluginsToInstall
@ -236,24 +204,9 @@ class Start extends Component {
return (
<Fragment>
{ showUsageModal && (
<UsageModal
onContinue={ () =>
continueAction === 'wizard'
? this.startWizard()
: this.skipWizard()
}
onClose={ () =>
this.setState( {
showUsageModal: false,
continueAction: '',
} )
}
/>
) }
<H className="woocommerce-profile-wizard__header-title">
{ __(
'Start setting up your WooCommerce store',
'Start enhancing your WooCommerce store',
'woocommerce-admin'
) }
</H>
@ -304,12 +257,8 @@ class Start extends Component {
<Button
isPrimary
onClick={ () =>
this.setState( {
showUsageModal: true,
continueAction: 'wizard',
} )
}
isBusy={ isPending }
onClick={ this.startPluginInstall }
className="woocommerce-profile-wizard__continue"
>
{ __( 'Get started', 'woocommerce-admin' ) }
@ -320,13 +269,9 @@ class Start extends Component {
<p>
<Button
isLink
isBusy={ isPending }
className="woocommerce-profile-wizard__skip"
onClick={ () =>
this.setState( {
showUsageModal: true,
continueAction: 'skip',
} )
}
onClick={ this.skipPluginInstall }
>
{ sprintf(
__( 'Proceed without %s', 'woocommerce-admin' ),
@ -347,7 +292,7 @@ export default compose(
getActivePlugins,
getOptions,
getProfileItems,
isJetpackConnected,
isGetProfileItemsRequesting,
} = select( 'wc-api' );
const isProfileItemsError = Boolean( getProfileItemsError() );
@ -361,13 +306,16 @@ export default compose(
const activePlugins = getActivePlugins();
const profileItems = getProfileItems();
const isJetpackActive = activePlugins.includes( 'jetpack' );
const isWcsActive = activePlugins.includes( 'woocommerce-services' );
return {
isJetpackActive,
isProfileItemsError,
activePlugins,
isWcsActive,
tosAccepted,
profileItems,
isJetpackConnected: isJetpackConnected(),
isRequesting: isGetProfileItemsRequesting(),
};
} ),
withDispatch( ( dispatch ) => {
@ -380,4 +328,4 @@ export default compose(
updateOptions,
};
} )
)( Start );
)( Benefits );

View File

@ -12,8 +12,7 @@ import { withDispatch } from '@wordpress/data';
* WooCommerce dependencies
*/
import { H, Stepper, Card } from '@woocommerce/components';
import { getNewPath, updateQueryString } from '@woocommerce/navigation';
import { getAdminLink, getSetting } from '@woocommerce/wc-admin-settings';
import { getSetting } from '@woocommerce/wc-admin-settings';
/**
* Internal dependencies
@ -39,9 +38,10 @@ class Plugins extends Component {
}
componentDidMount() {
const { isJetpackConnected } = this.props;
if ( plugins.length === 0 && isJetpackConnected ) {
return updateQueryString( { step: 'store-details' } );
const { goToNextStep } = this.props;
if ( plugins.length === 0 ) {
goToNextStep();
return;
}
this.props.installPlugins( plugins );
@ -51,16 +51,11 @@ class Plugins extends Component {
const {
createNotice,
errors,
goToNextStep,
installedPlugins,
activatedPlugins,
jetpackConnectUrl,
isJetpackConnected,
} = this.props;
if ( jetpackConnectUrl ) {
window.location = jetpackConnectUrl;
}
const newErrors = difference( errors, prevProps.errors );
newErrors.map( ( error ) => createNotice( 'error', error ) );
@ -73,16 +68,12 @@ class Plugins extends Component {
/* eslint-enable react/no-did-update-set-state */
}
// If Jetpack was already connected, we can go to store details after WCS is activated.
// Complete this step if all plugins are active.
if (
! plugins.includes( 'jetpack' ) &&
prevProps.activatedPlugins.length !== plugins.length &&
activatedPlugins.length === plugins.length &&
isJetpackConnected
activatedPlugins.length === plugins.length
) {
/* eslint-disable react/no-did-update-set-state */
return updateQueryString( { step: 'store-details' } );
/* eslint-enable react/no-did-update-set-state */
goToNextStep();
}
}
@ -101,40 +92,13 @@ class Plugins extends Component {
}
render() {
const {
hasErrors,
isRequesting,
isJetpackConnected,
jetpackConnectUrl,
} = this.props;
const { hasErrors, isRequesting } = this.props;
const { step } = this.state;
if ( plugins.length === 0 && ! isJetpackConnected ) {
return (
<Fragment>
<H className="woocommerce-profile-wizard__header-title">
{ __( 'Connecting your store', 'woocommerce-admin' ) }
</H>
<p>
{ __(
'You will be redirected to WordPress.com to continue connecting your site.',
'woocommerce-admin'
) }{ ' ' }
</p>
</Fragment>
);
}
const pluginLabel = plugins
.map( ( pluginSlug ) => pluginNames[ pluginSlug ] )
.join( ' & ' );
// Shows the "Activate & continue" button as busy during the request and redirect, so users see feedback.
const activateButtonBusy =
isRequesting ||
( ! isJetpackConnected && jetpackConnectUrl !== null );
return (
<Fragment>
<H className="woocommerce-profile-wizard__header-title">
@ -177,7 +141,7 @@ class Plugins extends Component {
{ ! hasErrors && step === 'activate' && (
<Button
isPrimary
isBusy={ activateButtonBusy }
isBusy={ isRequesting }
onClick={ this.activatePlugins }
>
{ __(
@ -196,16 +160,12 @@ class Plugins extends Component {
export default compose(
withSelect( ( select ) => {
const {
getJetpackConnectUrl,
isGetJetpackConnectUrlRequesting,
getJetpackConnectUrlError,
getPluginInstallations,
getPluginInstallationErrors,
getPluginActivations,
getPluginActivationErrors,
isPluginActivateRequesting,
isPluginInstallRequesting,
isJetpackConnected,
} = select( 'wc-api' );
const activationErrors = getPluginActivationErrors( plugins );
const activatedPlugins = Object.keys( getPluginActivations( plugins ) );
@ -214,28 +174,6 @@ export default compose(
getPluginInstallations( plugins )
);
const queryArgs = {
redirect_url: getAdminLink(
getNewPath( { step: 'store-details' } )
),
};
let jetpackConnectUrl = null;
let isJetpackConnectUrlRequesting = false;
let jetpackConnectUrlError = null;
if (
activatedPlugins.includes( 'jetpack' ) ||
( plugins.length === 0 && ! isJetpackConnected() ) ||
( activatedPlugins.includes( 'woocommerce-services' ) &&
! isJetpackConnected() )
) {
isJetpackConnectUrlRequesting = isGetJetpackConnectUrlRequesting(
queryArgs
);
jetpackConnectUrl = getJetpackConnectUrl( queryArgs );
jetpackConnectUrlError = getJetpackConnectUrlError( queryArgs );
}
const errors = [];
Object.keys( activationErrors ).map( ( plugin ) =>
errors.push( activationErrors[ plugin ].message )
@ -243,25 +181,17 @@ export default compose(
Object.keys( installationErrors ).map( ( plugin ) =>
errors.push( installationErrors[ plugin ].message )
);
if ( jetpackConnectUrlError ) {
errors.push( jetpackConnectUrlError.message );
}
const hasErrors = Boolean( errors.length );
const isRequesting =
isPluginActivateRequesting() ||
isPluginInstallRequesting() ||
isJetpackConnectUrlRequesting;
isPluginActivateRequesting() || isPluginInstallRequesting();
return {
activatedPlugins,
installedPlugins,
jetpackConnectUrl,
isJetpackConnectUrlRequesting,
errors,
hasErrors,
isRequesting,
isJetpackConnected: isJetpackConnected(),
};
} ),
withDispatch( ( dispatch ) => {

View File

@ -73,15 +73,8 @@ class StoreDetails extends Component {
return currencyData[ region ] || currencyData.US;
}
onSubmit( values ) {
const { profileItems } = this.props;
if ( profileItems.plugins === 'already-installed' ) {
this.setState( { showUsageModal: true } );
return;
}
this.onContinue( values );
onSubmit() {
this.setState( { showUsageModal: true } );
}
async onContinue( values ) {