Skip to content

Commit

Permalink
Assistant: Add VaultPress card to the assistant flow (#28741)
Browse files Browse the repository at this point in the history
* Assistant: add VaultPress upsell screen

* changelog

* Assistant: add dynamic price to VaultPress card button

* Assistant: add VaultPress as the last step

* Assistant: update style to match proposed design

* Assistant: fix e2e tests for the recommendation steps

* Remove site-products reducer from exclude list

* Remove accidentally added changelog

* Assistant: fix description list style

* Assistant: adjust progressValue

---------

Co-authored-by: Samiff <[email protected]>
  • Loading branch information
IanRamosC and samiff authored Feb 23, 2023
1 parent f222193 commit cee144a
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 30 deletions.
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/_inc/client/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ const recommendationsRoutes = [
'/recommendations/backup-plan',
'/recommendations/boost',
'/recommendations/summary',
'/recommendations/vaultpress-backup',
'/recommendations/vaultpress-for-woocommerce',
];

const dashboardRoutes = [ '/', '/dashboard', '/reconnect', '/my-plan', '/plans' ];
Expand Down Expand Up @@ -503,6 +505,8 @@ class Main extends React.Component {
case '/recommendations/backup-plan':
case '/recommendations/boost':
case '/recommendations/summary':
case '/recommendations/vaultpress-backup':
case '/recommendations/vaultpress-for-woocommerce':
case '/recommendations/welcome-backup':
case '/recommendations/welcome-complete':
case '/recommendations/welcome-security':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const RECOMMENDATION_WIZARD_STEP = {
RELATED_POSTS: 'related-posts',
CREATIVE_MAIL: 'creative-mail',
SITE_ACCELERATOR: 'site-accelerator',
VAULTPRESS_BACKUP: 'vaultpress-backup',
VAULTPRESS_FOR_WOOCOMMERCE: 'vaultpress-for-woocommerce',
PUBLICIZE: 'publicize',
PROTECT: 'protect',
ANTI_SPAM: 'anti-spam',
Expand Down
107 changes: 101 additions & 6 deletions projects/plugins/jetpack/_inc/client/recommendations/feature-utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import formatCurrency from '@automattic/format-currency';
import restApi from '@automattic/jetpack-api';
import { getRedirectUrl } from '@automattic/jetpack-components';
import { __ } from '@wordpress/i18n';
import { sprintf, __ } from '@wordpress/i18n';
import {
PLAN_JETPACK_SECURITY_T1_YEARLY,
PLAN_JETPACK_VIDEOPRESS,
Expand All @@ -16,6 +17,11 @@ import {
import { updateSettings } from 'state/settings';
import { fetchPluginsData } from 'state/site/plugins';
import { isFeatureActive } from '../state/recommendations';
import {
getSiteProduct,
getSiteProductMonthlyCost,
isFetchingSiteProducts,
} from '../state/site-products';

export const mapStateToSummaryFeatureProps = ( state, featureSlug ) => {
switch ( featureSlug ) {
Expand Down Expand Up @@ -115,6 +121,11 @@ export const getSummaryResourceProps = ( state, resourceSlug ) => {
ctaLabel: __( 'Add', 'jetpack' ),
ctaLink: getJetpackCloudUrl( state, 'settings' ),
};
case 'vaultpress-backup':
case 'vaultpress-for-woocommerce':
return {
displayName: __( 'VaultPress Backup', 'jetpack' ),
};
default:
throw `Unknown resource slug in getSummaryResourceProps() recommendations/feature-utils.js: ${ resourceSlug }`;
}
Expand Down Expand Up @@ -281,7 +292,7 @@ export const getStepContent = ( state, stepSlug ) => {
};
case 'creative-mail':
return {
progressValue: '86',
progressValue: '76',
question: __( 'Would you like to turn site visitors into subscribers?', 'jetpack' ),
description: __(
'The Jetpack Newsletter Form combined with Creative Mail by Constant Contact can help automatically gather subscribers and send them beautiful emails. <ExternalLink>Learn more</ExternalLink>',
Expand All @@ -294,7 +305,7 @@ export const getStepContent = ( state, stepSlug ) => {
};
case 'monitor':
return {
progressValue: '57',
progressValue: '52',
question: __(
'Would you like Downtime Monitoring to notify you if your site goes offline?',
'jetpack'
Expand All @@ -309,7 +320,7 @@ export const getStepContent = ( state, stepSlug ) => {
};
case 'related-posts':
return {
progressValue: '71',
progressValue: '64',
question: __(
'Would you like Related Posts to display at the bottom of your content?',
'jetpack'
Expand All @@ -324,7 +335,7 @@ export const getStepContent = ( state, stepSlug ) => {
};
case 'site-accelerator':
return {
progressValue: '99',
progressValue: '88',
question: __( 'Would you like your site to load faster?', 'jetpack' ),
description: __(
'Faster sites get better ranking in search engines and help keep visitors on your site longer. Jetpack will automatically optimize and load your images and files from our global Content Delivery Network (CDN). <ExternalLink>Learn more</ExternalLink>',
Expand Down Expand Up @@ -383,7 +394,7 @@ export const getStepContent = ( state, stepSlug ) => {
};
case 'woocommerce':
return {
progressValue: '43',
progressValue: '40',
question: __( 'Would you like WooCommerce to power your store?', 'jetpack' ),
description: __(
'We’re partnered with <strong>WooCommerce</strong> — a customizable, open-source eCommerce platform built for WordPress. It’s everything you need to start selling products today. <ExternalLink>Learn more</ExternalLink>',
Expand Down Expand Up @@ -547,6 +558,90 @@ export const getStepContent = ( state, stepSlug ) => {
ctaLink: getJetpackCloudUrl( state, 'settings' ),
illustration: 'assistant-server-credentials',
};
case 'vaultpress-backup': {
const siteRawUrl = getSiteRawUrl( state );
const monthlyPrice = getSiteProductMonthlyCost( state, PLAN_JETPACK_BACKUP_T1_YEARLY );
const product = getSiteProduct( state, PLAN_JETPACK_BACKUP_T1_YEARLY );
const price = formatCurrency( monthlyPrice, product?.currency_code );
const ctaText = isFetchingSiteProducts( state )
? __( 'Try for 30 days', 'jetpack' )
: sprintf(
/* translators: %s: is a formatted currency. e.g. $1 */
__( 'Try for %s for 30 days', 'jetpack' ),
price
);

return {
progressValue: 100,
question: __(
'Never lose your site, even if your host goes down (along with your backups)',
'jetpack'
),
description: '',
descriptionList: [
__(
'VaultPress Backup is built specifically for WordPress and has done over 270 million backups to date.',
'jetpack'
),
__(
'We store copies of your backups in our secure cloud, so your content will never be lost.',
'jetpack'
),
__(
'If your site goes down, you can restore it with one click from desktop or the Jetpack mobile app.',
'jetpack'
),
__( 'VaultPress Backup is so easy to use; no developer required.', 'jetpack' ),
],
ctaText: ctaText,
ctaLink: getRedirectUrl( 'jetpack-recommendations-product-checkout', {
site: siteRawUrl,
path: PLAN_JETPACK_BACKUP_T1_YEARLY,
} ),
illustration: 'assistant-backup-welcome',
};
}
case 'vaultpress-for-woocommerce': {
const siteRawUrl = getSiteRawUrl( state );
const monthlyPrice = getSiteProductMonthlyCost( state, PLAN_JETPACK_BACKUP_T1_YEARLY );
const product = getSiteProduct( state, PLAN_JETPACK_BACKUP_T1_YEARLY );
const price = formatCurrency( monthlyPrice, product?.currency_code );
const ctaText = isFetchingSiteProducts( state )
? __( 'Try for 30 days', 'jetpack' )
: sprintf(
/* translators: %s: is a formatted currency. e.g. $1 */
__( 'Try for %s for 30 days', 'jetpack' ),
price
);

return {
progressValue: 100,
question: __(
'Store downtime means lost sales. Do you have a cloud-based store backup solution?',
'jetpack'
),
description: __(
'VaultPress Backup saves your store in the cloud, so even if your host goes down, you’ll never lose a thing.',
'jetpack'
),
descriptionList: [
__(
'Restore your site to any past state in one click while keeping all orders and products current.',
'jetpack'
),
__( 'Backups are encrypted, keeping your store data secure.', 'jetpack' ),
__( 'Protect your customer data and stay GDPR compliant.', 'jetpack' ),
__( 'Custom WooCommerce table backups.', 'jetpack' ),
__( 'Easy to use; no developer required.', 'jetpack' ),
],
ctaText: ctaText,
ctaLink: getRedirectUrl( 'jetpack-recommendations-product-checkout', {
site: siteRawUrl,
path: PLAN_JETPACK_BACKUP_T1_YEARLY,
} ),
illustration: 'assistant-backup-welcome',
};
}
default:
throw `Unknown step slug in recommendations/question: ${ stepSlug }`;
}
Expand Down
14 changes: 14 additions & 0 deletions projects/plugins/jetpack/_inc/client/recommendations/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
updateRecommendationsStep as updateRecommendationsStepAction,
} from 'state/recommendations';
import { isFetchingSiteData } from 'state/site';
import QuerySiteProducts from '../components/data/query-site-products';
import { RECOMMENDATION_WIZARD_STEP } from './constants';
import { ProductPurchased } from './product-purchased';
import { FeaturePrompt } from './prompts/feature-prompt';
Expand Down Expand Up @@ -90,6 +91,12 @@ const RecommendationsComponent = props => {
case RECOMMENDATION_WIZARD_STEP.SITE_ACCELERATOR:
redirectPath = '/site-accelerator';
break;
case RECOMMENDATION_WIZARD_STEP.VAULTPRESS_BACKUP:
redirectPath = '/vaultpress-backup';
break;
case RECOMMENDATION_WIZARD_STEP.VAULTPRESS_FOR_WOOCOMMERCE:
redirectPath = '/vaultpress-for-woocommerce';
break;
case RECOMMENDATION_WIZARD_STEP.PUBLICIZE:
redirectPath = '/publicize';
break;
Expand Down Expand Up @@ -173,6 +180,7 @@ const RecommendationsComponent = props => {
<QuerySite />
<QuerySitePlugins />
<QuerySiteDiscount />
<QuerySiteProducts />
<QueryIntroOffers />
{ isLoading ? (
<div className="jp-recommendations__loading">
Expand Down Expand Up @@ -209,6 +217,12 @@ const RecommendationsComponent = props => {
<Route path="/recommendations/site-accelerator">
<FeaturePrompt stepSlug="site-accelerator" />
</Route>
<Route path="/recommendations/vaultpress-backup">
<ResourcePrompt stepSlug="vaultpress-backup" />
</Route>
<Route path="/recommendations/vaultpress-for-woocommerce">
<ResourcePrompt stepSlug="vaultpress-for-woocommerce" />
</Route>
<Route path="/recommendations/publicize">
<FeaturePrompt stepSlug="publicize" isNew={ isNew( 'publicize' ) } />
</Route>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ const PromptLayoutComponent = props => {
nbsp: <span>&nbsp;</span>,
} ) }
</h1>
<p className="jp-recommendations-question__description">{ description }</p>
{ description && (
<p className="jp-recommendations-question__description">{ description }</p>
) }
{ content }
<div className="jp-recommendations-question__answer">{ answer }</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,22 @@
color: var( --jp-gray-80 );

font-size: 1.5rem;
margin: 40px 40px 0 40px;
margin: 2.5rem 2.5rem 1rem 2.5rem;

@include breakpoint( '<480px' ) {
margin: 0 16px 32px 16px;
margin: 0 1rem 2rem 1rem;
}
}

.jp-recommendations-question__description {
color: var( --jp-gray-100 );

font-size: 16px;
font-size: 1rem;

margin: 32px 32px 32px 40px;
margin: 1rem 2rem 2rem 2.5rem;

@include breakpoint( '<480px' ) {
margin: 0 16px 16px 16px;
margin: 0 1rem 1rem 1rem;
}

.gridicons-external {
Expand All @@ -66,13 +66,21 @@

.jp-recommendations-question__description-list {
color: var( --jp-gray-100 );
font-size: 16px;
margin: 0 32px 32px 32px;
list-style: disc;
padding-left: 16px;
font-size: 1rem;
margin: 0 2rem 2rem 2.5rem;

list-style-type: none;
padding: 0;

li {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEVHcEwFnwUInggGnggGnggHnAcAnwUFnQcAnwcGnwkFnQgGnQgFnwcGnQYFnQcFnAcGnQkDnwdhiL0pAAAAEnRSTlMAMF//f2Aw7yBQ3+9gcIBgcED+HDbkAAAAZklEQVR4Ae3LNwICARDDQC0+cv7/Y8mwV9odSfWIcf/+VegnGkIvDaGXKvTTn/Gz+Uf5xTL0K1XotS7fs5H6GHvvaO8d7c3j7rdgHne/A/PYt/cO+R42oYdN6OEQetiFHo4A//6dAXqtBEkmtWutAAAAAElFTkSuQmCC) no-repeat;
background-size: 1.25rem;
padding-left: 1.75rem;
margin-bottom: 0.5rem;
}

@include breakpoint( '<480px' ) {
margin: 0 16px 0 16px;
margin: 0 1rem 0 1rem;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,17 @@ const ResourcePromptComponent = props => {
progressBar={ progressBarComponent }
isNew={ isNew }
question={ question }
description={ createInterpolateElement( description, {
br: <br />,
strong: <strong />,
ExternalLink: <ExternalLink href={ descriptionLink } onClick={ onExternalLinkClick } />,
} ) }
description={
description
? createInterpolateElement( description, {
br: <br />,
strong: <strong />,
ExternalLink: (
<ExternalLink href={ descriptionLink } onClick={ onExternalLinkClick } />
),
} )
: null
}
content={
descriptionList || descriptionSecondary ? (
<React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PLAN_JETPACK_VIDEOPRESS,
PLAN_JETPACK_ANTI_SPAM,
PLAN_JETPACK_BACKUP_T1_YEARLY,
getPlanClass,
} from 'lib/plans/constants';
import { assign, difference, get, isArray, isEmpty, mergeWith, union } from 'lodash';
import {
Expand Down Expand Up @@ -50,6 +51,7 @@ import {
getNewRecommendations,
getInitialRecommendationsStep,
getNewRecommendationsCount,
isWooCommerceActive,
} from 'state/initial-state';
import { getRewindStatus } from 'state/rewind';
import { getSetting } from 'state/settings';
Expand Down Expand Up @@ -335,7 +337,9 @@ const stepToNextStepByPath = {
'related-posts': 'creative-mail',
'creative-mail': 'site-accelerator',
'site-accelerator': 'publicize',
publicize: 'summary',
publicize: 'vaultpress-for-woocommerce',
'vaultpress-for-woocommerce': 'vaultpress-backup', // falls back to vaultpress-backup so it only shows one of them
'vaultpress-backup': 'summary',
protect: 'summary',
'anti-spam': 'summary',
videopress: 'summary',
Expand Down Expand Up @@ -410,6 +414,8 @@ export const stepToRoute = {
'backup-plan': '#/recommendations/backup-plan',
boost: '#/recommendations/boost',
summary: '#/recommendations/summary',
'vaultpress-backup': '#/recommendations/vaultpress-backup',
'vaultpress-for-woocommerce': '#/recommendations/vaultpress-for-woocommerce',
// new steps (September 2022)
welcome__backup: '#/recommendations/welcome-backup',
welcome__complete: '#/recommendations/welcome-complete',
Expand Down Expand Up @@ -547,6 +553,13 @@ export const getProductSlugForStep = ( state, step ) => {
return false;
};

const shouldRecommendVaultPress = ( state, isWooCommerceRequired = false ) => {
const sitePlan = getSitePlan( state ).product_slug;
const isFree = 'is-free-plan' === getPlanClass( sitePlan );

return isFree && isWooCommerceActive( state ) === isWooCommerceRequired;
};

const isConditionalRecommendationEnabled = ( state, step ) => {
const conditionalRecommendations = getConditionalRecommendations( state );
return (
Expand All @@ -565,6 +578,10 @@ const isStepEligibleToShow = ( state, step ) => {
return true;
case 'product-suggestions':
return isProductSuggestionsAvailable( state );
case 'vaultpress-backup':
return shouldRecommendVaultPress( state );
case 'vaultpress-for-woocommerce':
return shouldRecommendVaultPress( state, true );
case 'agency':
return !! getDataByKey( state, 'site-type-agency' );
case 'woocommerce':
Expand Down
Loading

0 comments on commit cee144a

Please sign in to comment.