Add launch your store success screen (#46103)
* Initial commit with LYS components * Update CustomerFeedbackSimple component to support emoji value props * Add confetti package in woocommerce components * Add confetti usage * Remove unnecessary files * Update pnpm lock * Changelogs * Lint and temporarily comment out tests * Lint css and rename image * Various fixes * Rename transitional to congrats * Add copy link functionaility from Moon's code and move whatsnext component * Rename components * Move and renames * Fix ref type * Add temporary dynamic actions * Remove confetti background * Add header * Update xstate actions for launch success page * Add temporary spinner * Combine congrats data fetching to a single action and service * Add functioning dynamic actions list * Temporarily remove test * Cleanups * More cleanups * Small lint * add url listener for content param * Update comment on confetti package * Remove lodash and replace with reduce * Fix Woo Express condition --------- Co-authored-by: rjchow <me@rjchow.com>
This commit is contained in:
parent
c551667646
commit
7e7be4f9d0
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add confetti component and dependency from canvas-confetti package
|
|
@ -97,6 +97,7 @@
|
|||
"@wordpress/rich-text": "wp-6.0",
|
||||
"@wordpress/url": "wp-6.0",
|
||||
"@wordpress/viewport": "^4.20.0",
|
||||
"canvas-confetti": "^1.9.2",
|
||||
"classnames": "^2.3.2",
|
||||
"core-js": "^3.34.0",
|
||||
"d3-axis": "^1.0.12",
|
||||
|
@ -148,6 +149,7 @@
|
|||
"@testing-library/react": "12.1.3",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/user-event": "13.5.0",
|
||||
"@types/canvas-confetti": "^1.6.4",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/prop-types": "^15.7.11",
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import confetti from 'canvas-confetti';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Note: This was copied over from https://github.com/Automattic/wp-calypso/blob/a39539547780871d0371a20fcf21c767a86a1010/packages/components/src/confetti/index.ts
|
||||
* since there was problems with importing @automattic/components:2.1.0 due to versions of its dependencies breaking wc core.
|
||||
* If we do not end up making further adjustments in this file that are not supported by the original implementation,
|
||||
* we should consider replacing it with the npm package when we're able to.
|
||||
*/
|
||||
|
||||
const COLORS = [
|
||||
'#31CC9F',
|
||||
'#618DF2',
|
||||
'#6AB3D0',
|
||||
'#B35EB1',
|
||||
'#F2D76B',
|
||||
'#FAA754',
|
||||
'#E34C84',
|
||||
];
|
||||
|
||||
type FireOptions = {
|
||||
spread: number;
|
||||
startVelocity?: number;
|
||||
decay?: number;
|
||||
scalar?: number;
|
||||
};
|
||||
|
||||
function fireConfetti( colors: string[] ) {
|
||||
const count = 60;
|
||||
const scale = 2;
|
||||
const defaults = {
|
||||
origin: { y: 0.4 },
|
||||
colors,
|
||||
scalar: scale,
|
||||
spread: 180,
|
||||
gravity: 6,
|
||||
};
|
||||
|
||||
function fire( particleRatio: number, opts: FireOptions ) {
|
||||
confetti(
|
||||
Object.assign( {}, defaults, opts, {
|
||||
particleCount: Math.floor( count * particleRatio ),
|
||||
startVelocity: opts.startVelocity
|
||||
? scale * opts.startVelocity
|
||||
: undefined,
|
||||
spread: scale * opts.spread,
|
||||
scalar: opts.scalar ? scale * opts.scalar : scale,
|
||||
// counter react-modal very high z index, always render the confetti on top
|
||||
zIndex: 1000000,
|
||||
} )
|
||||
);
|
||||
}
|
||||
|
||||
fire( 0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
} );
|
||||
fire( 0.2, {
|
||||
spread: 60,
|
||||
} );
|
||||
fire( 0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8,
|
||||
} );
|
||||
fire( 0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2,
|
||||
} );
|
||||
fire( 0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
} );
|
||||
}
|
||||
|
||||
export const ConfettiAnimation = ( {
|
||||
trigger = true,
|
||||
delay = 0,
|
||||
colors = COLORS,
|
||||
} ) => {
|
||||
useEffect( () => {
|
||||
if ( trigger ) {
|
||||
setTimeout( () => fireConfetti( colors ), delay );
|
||||
}
|
||||
}, [ trigger, delay, colors ] );
|
||||
|
||||
return null;
|
||||
};
|
|
@ -113,3 +113,4 @@ export {
|
|||
} from './product-section-layout';
|
||||
export { DisplayState } from './display-state';
|
||||
export { ProgressBar } from './progress-bar';
|
||||
export { ConfettiAnimation } from './confetti-animation';
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Add value props to CustomerFeedbackSimple component
|
|
@ -6,10 +6,12 @@ import PropTypes from 'prop-types';
|
|||
import { Button, Tooltip } from '@wordpress/components';
|
||||
import { Text } from '@woocommerce/experimental';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
|
||||
type CustomerFeedbackSimpleProps = {
|
||||
onSelect: ( score: number ) => void;
|
||||
label: string;
|
||||
selectedValue?: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -23,13 +25,15 @@ type CustomerFeedbackSimpleProps = {
|
|||
*
|
||||
* Upon completion, the score and comments is sent to a callback function.
|
||||
*
|
||||
* @param {Object} props Component props.
|
||||
* @param {Function} props.onSelect Function to call when the results are sent.
|
||||
* @param {string} props.label Question to ask the customer.
|
||||
* @param {Object} props Component props.
|
||||
* @param {Function} props.onSelect Function to call when the results are sent.
|
||||
* @param {string} props.label Question to ask the customer.
|
||||
* @param {number|null} [props.selectedValue] The default selected value.
|
||||
*/
|
||||
const CustomerFeedbackSimple: React.FC< CustomerFeedbackSimpleProps > = ( {
|
||||
onSelect,
|
||||
label,
|
||||
selectedValue,
|
||||
} ) => {
|
||||
const options = [
|
||||
{
|
||||
|
@ -76,6 +80,9 @@ const CustomerFeedbackSimple: React.FC< CustomerFeedbackSimpleProps > = ( {
|
|||
onClick={ () => {
|
||||
onSelect( option.value );
|
||||
} }
|
||||
className={ classNames( {
|
||||
'is-selected': selectedValue === option.value,
|
||||
} ) }
|
||||
>
|
||||
{ option.emoji }
|
||||
</Button>
|
||||
|
@ -89,6 +96,7 @@ const CustomerFeedbackSimple: React.FC< CustomerFeedbackSimpleProps > = ( {
|
|||
CustomerFeedbackSimple.propTypes = {
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
selectedValue: PropTypes.number,
|
||||
};
|
||||
|
||||
export { CustomerFeedbackSimple };
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { MainContentComponentProps } from '../xstate';
|
||||
export const LaunchYourStoreSuccess = ( props: MainContentComponentProps ) => {
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
'launch-store-success-page__container',
|
||||
props.className
|
||||
) }
|
||||
>
|
||||
<p>Main Content - Site Launch Store Success Page</p>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,277 @@
|
|||
/* eslint-disable @woocommerce/dependency-group */
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { createInterpolateElement, useState } from '@wordpress/element';
|
||||
import { Link, ConfettiAnimation } from '@woocommerce/components';
|
||||
import { isInteger } from 'lodash';
|
||||
import { closeSmall } from '@wordpress/icons';
|
||||
import { CustomerFeedbackSimple } from '@woocommerce/customer-effort-score';
|
||||
import { useCopyToClipboard } from '@wordpress/compose';
|
||||
import { Button, TextareaControl, Icon, Dashicon } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import WooLogo from '~/core-profiler/components/navigation/woologo';
|
||||
import { navigateTo } from '@woocommerce/navigation';
|
||||
|
||||
export type CongratsProps = {
|
||||
hasCompleteSurvey: boolean;
|
||||
isWooExpress: boolean;
|
||||
completeSurvey: () => void;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const Congrats = ( {
|
||||
hasCompleteSurvey,
|
||||
isWooExpress,
|
||||
completeSurvey,
|
||||
children,
|
||||
}: CongratsProps ) => {
|
||||
const copyLink = __( 'Copy link', 'woocommerce' );
|
||||
const copied = __( 'Copied!', 'woocommerce' );
|
||||
const homeUrl: string = getSetting( 'homeUrl', '' );
|
||||
const urlObject = new URL( homeUrl );
|
||||
let hostname: string = urlObject?.hostname;
|
||||
if ( urlObject?.port ) {
|
||||
hostname += ':' + urlObject.port;
|
||||
}
|
||||
|
||||
const [ isShowSurvey, setIsShowSurvey ] = useState< boolean >(
|
||||
! hasCompleteSurvey
|
||||
);
|
||||
const [ emojiValue, setEmojiValue ] = useState< number | null >( null );
|
||||
const [ feedbackText, setFeedbackText ] = useState< string >( '' );
|
||||
const [ isShowThanks, setIsShowThanks ] = useState< boolean >( false );
|
||||
const [ copyLinkText, setCopyLinkText ] = useState( copyLink );
|
||||
|
||||
const shouldShowComment = isInteger( emojiValue );
|
||||
|
||||
const copyClipboardRef = useCopyToClipboard< HTMLAnchorElement >(
|
||||
homeUrl,
|
||||
() => {
|
||||
setCopyLinkText( copied );
|
||||
setTimeout( () => {
|
||||
setCopyLinkText( copyLink );
|
||||
}, 2000 );
|
||||
}
|
||||
);
|
||||
|
||||
const sendData = () => {
|
||||
const emojis = {
|
||||
1: 'very_difficult',
|
||||
2: 'difficult',
|
||||
3: 'neutral',
|
||||
4: 'good',
|
||||
5: 'very_good',
|
||||
} as const;
|
||||
const emoji_value = emojiValue
|
||||
? emojis[ emojiValue as keyof typeof emojis ]
|
||||
: 'none';
|
||||
recordEvent( 'launch_your_store_congrats_survey_complete', {
|
||||
emoji: emoji_value,
|
||||
feedback: feedbackText,
|
||||
} );
|
||||
|
||||
setIsShowThanks( true );
|
||||
completeSurvey();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="woocommerce-launch-store__congrats">
|
||||
<ConfettiAnimation delay={ 1000 } />
|
||||
<div className="woocommerce-launch-store__congrats-header-container">
|
||||
<span className="woologo">
|
||||
<WooLogo />
|
||||
</span>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
navigateTo( { url: '/' } );
|
||||
} }
|
||||
className="back-to-home-button"
|
||||
variant="link"
|
||||
>
|
||||
<Dashicon icon="arrow-left-alt2"></Dashicon>
|
||||
<span>{ __( 'Back to Home', 'woocommerce' ) }</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="woocommerce-launch-store__congrats-content">
|
||||
<h1 className="woocommerce-launch-store__congrats-heading">
|
||||
{ __(
|
||||
'Congratulations! Your store is now live',
|
||||
'woocommerce'
|
||||
) }
|
||||
</h1>
|
||||
<h2 className="woocommerce-launch-store__congrats-subheading">
|
||||
{ __(
|
||||
"You've successfully launched your store and are ready to start selling! We can't wait to see your business grow.",
|
||||
'woocommerce'
|
||||
) }
|
||||
</h2>
|
||||
<div className="woocommerce-launch-store__congrats-midsection-container">
|
||||
<div className="woocommerce-launch-store__congrats-visit-store">
|
||||
<p className="store-name">{ hostname }</p>
|
||||
<div className="buttons-container">
|
||||
<Button
|
||||
className=""
|
||||
variant="secondary"
|
||||
ref={ copyClipboardRef }
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'launch_you_store_congrats_copy_store_link_click'
|
||||
);
|
||||
} }
|
||||
>
|
||||
{ copyLinkText }
|
||||
</Button>
|
||||
<Button
|
||||
className=""
|
||||
variant="primary"
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
'launch_you_store_congrats_preview_store_click'
|
||||
);
|
||||
window.open( homeUrl, '_blank' );
|
||||
} }
|
||||
>
|
||||
{ __( 'Visit your store', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ isShowSurvey && <hr className="separator" /> }
|
||||
|
||||
{ isShowSurvey && (
|
||||
<div className="woocommerce-launch-store__congrats-survey">
|
||||
{ isShowThanks ? (
|
||||
<div className="woocommerce-launch-store__congrats-thanks">
|
||||
<p className="thanks-copy">
|
||||
🙌{ ' ' }
|
||||
{ __(
|
||||
'We appreciate your feedback!',
|
||||
'woocommerce'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
className="close-button"
|
||||
label={ __( 'Close', 'woocommerce' ) }
|
||||
icon={
|
||||
<Icon
|
||||
icon={ closeSmall }
|
||||
viewBox="6 4 12 14"
|
||||
/>
|
||||
}
|
||||
iconSize={ 14 }
|
||||
size={ 24 }
|
||||
onClick={ () => {
|
||||
setIsShowThanks( false );
|
||||
setIsShowSurvey( false );
|
||||
} }
|
||||
></Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="woocommerce-launch-store__congrats-section_1">
|
||||
<div className="woocommerce-launch-store__congrats-survey__selection">
|
||||
<CustomerFeedbackSimple
|
||||
label={ __(
|
||||
'How was the experience of launching your store?',
|
||||
'woocommerce'
|
||||
) }
|
||||
onSelect={ ( score ) =>
|
||||
setEmojiValue( score )
|
||||
}
|
||||
selectedValue={ emojiValue }
|
||||
/>
|
||||
</div>
|
||||
{ shouldShowComment && (
|
||||
<div className="woocommerce-launch-store__congrats-survey__comment">
|
||||
<label
|
||||
className="comment-label"
|
||||
htmlFor="launch-your-store-comment"
|
||||
>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'Why do you feel that way? <smallText>(optional)</smallText>',
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
smallText: (
|
||||
<span className="small-text" />
|
||||
),
|
||||
}
|
||||
) }
|
||||
</label>
|
||||
<TextareaControl
|
||||
id="launch-your-store-comment"
|
||||
value={ feedbackText }
|
||||
onChange={ ( value ) => {
|
||||
setFeedbackText( value );
|
||||
} }
|
||||
/>
|
||||
<span className="privacy-text">
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'Your feedback will be only be shared with WooCommerce and treated in accordance with our <privacyLink>privacy policy</privacyLink>.',
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
privacyLink: (
|
||||
<Link
|
||||
href="https://automattic.com/privacy/"
|
||||
type="external"
|
||||
target="_blank"
|
||||
>
|
||||
<></>
|
||||
</Link>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</span>
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
{ shouldShowComment && ! isShowThanks && (
|
||||
<div className="woocommerce-launch-store__congrats-section_2">
|
||||
<div className="woocommerce-launch-store__congrats-buttons">
|
||||
<Button
|
||||
className=""
|
||||
variant="tertiary"
|
||||
onClick={ () => {
|
||||
setEmojiValue( null );
|
||||
} }
|
||||
>
|
||||
{ __( 'Cancel', 'woocommerce' ) }
|
||||
</Button>
|
||||
<Button
|
||||
className=""
|
||||
variant="primary"
|
||||
onClick={ () => {
|
||||
recordEvent(
|
||||
isWooExpress
|
||||
? 'launch_you_store_congrats_survey_click'
|
||||
: 'launch_you_store_on_core_congrats_survey_click'
|
||||
);
|
||||
sendData();
|
||||
} }
|
||||
>
|
||||
{ __( 'Send', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
{ children }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useMemo } from '@wordpress/element';
|
||||
import type { TaskListType } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ADMIN_URL } from '~/utils/admin-settings';
|
||||
|
||||
type WhatsNextProps = {
|
||||
activePlugins: string[];
|
||||
allTasklists: TaskListType[];
|
||||
};
|
||||
|
||||
type Action = {
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
linkText: string;
|
||||
trackEvent: string;
|
||||
};
|
||||
|
||||
const getActionsList = ( { activePlugins, allTasklists }: WhatsNextProps ) => {
|
||||
const actions: Action[] = [];
|
||||
const pick = ( action: Action, condition: boolean ) => {
|
||||
if ( actions.length < 3 && condition ) {
|
||||
actions.push( action );
|
||||
}
|
||||
};
|
||||
|
||||
const setupTasksCompletion = allTasklists
|
||||
.find( ( { id } ) => id === 'setup' )
|
||||
?.tasks?.reduce(
|
||||
( acc: Record< string, boolean >, { id, isComplete } ) => {
|
||||
acc[ id ] = isComplete || false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const extendedTasksCompletion = allTasklists
|
||||
.find( ( { id } ) => id === 'extended' )
|
||||
?.tasks?.reduce(
|
||||
( acc: Record< string, boolean >, { id, isComplete } ) => {
|
||||
acc[ id ] = isComplete || false;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const isMarketingTaskCompleted = setupTasksCompletion?.marketing || false;
|
||||
const isPaymentsTaskCompleted = setupTasksCompletion?.payments || false;
|
||||
const isMobileTaskCompleted =
|
||||
extendedTasksCompletion?.[ 'get-mobile-app' ] || false;
|
||||
const isMailChimpActivated = activePlugins.includes(
|
||||
'mailchimp-for-woocommerce'
|
||||
);
|
||||
|
||||
const marketing = {
|
||||
title: __( 'Promote your products', 'woocommerce' ),
|
||||
description: __(
|
||||
'Grow your customer base by promoting your products to millions of engaged shoppers.',
|
||||
'woocommerce'
|
||||
),
|
||||
link: `${ ADMIN_URL }admin.php?page=wc-admin&task=marketing`,
|
||||
linkText: __( 'Promote products', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_marketing_click',
|
||||
};
|
||||
|
||||
const payments = {
|
||||
title: __( 'Provide more ways to pay', 'woocommerce' ),
|
||||
description: __(
|
||||
'Give your shoppers more ways to pay by adding additional payment methods to your store.',
|
||||
'woocommerce'
|
||||
),
|
||||
link: `${ ADMIN_URL }admin.php?page=wc-admin&task=payments`,
|
||||
linkText: __( 'Add payment methods', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_payments_click',
|
||||
};
|
||||
|
||||
const mailchimp = {
|
||||
title: __( 'Build customer relationships', 'woocommerce' ),
|
||||
description: __(
|
||||
"Keep your shoppers up to date with what's new in your store and set up clever post-purchase automations.",
|
||||
'woocommerce'
|
||||
),
|
||||
link: isMailChimpActivated
|
||||
? `${ ADMIN_URL }admin.php?page=mailchimp-woocommerce`
|
||||
: 'https://woo.com/products/mailchimp-for-woocommerce/?utm_source=launch_your_store&utm_medium=product',
|
||||
linkText: isMailChimpActivated
|
||||
? __( 'Manage Mailchimp', 'woocommerce' )
|
||||
: __( 'Install Mailchimp', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_mailchimp_click',
|
||||
};
|
||||
|
||||
const extensions = {
|
||||
title: __( 'Power up your store', 'woocommerce' ),
|
||||
description: __(
|
||||
'Add extra features and functionality to your store with Woo extensions.',
|
||||
'woocommerce'
|
||||
),
|
||||
link: `${ ADMIN_URL }admin.php?page=wc-admin&path=%2Fextensions`,
|
||||
linkText: __( 'Add extensions', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_extensions_click',
|
||||
};
|
||||
|
||||
const mobileApp = {
|
||||
title: __( 'Manage your store on the go', 'woocommerce' ),
|
||||
description: __(
|
||||
'Manage your store anywhere with the free WooCommerce Mobile App.',
|
||||
'woocommerce'
|
||||
),
|
||||
link: `${ ADMIN_URL }admin.php?page=wc-admin&mobileAppModal=true`,
|
||||
linkText: __( 'Get the app', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_mobile_app_click',
|
||||
};
|
||||
|
||||
const externalDocumentation = {
|
||||
title: __( 'Help is on hand', 'woocommerce' ),
|
||||
description: __(
|
||||
"Detailed guides and our support team are always available if you're feeling stuck or need some guidance.",
|
||||
'woocommerce'
|
||||
),
|
||||
link: `https://woo.com/documentation/woocommerce/?utm_source=launch_your_store&utm_medium=product`,
|
||||
linkText: __( 'Explore support resources', 'woocommerce' ),
|
||||
trackEvent: 'launch_you_store_congrats_external_documentation_click',
|
||||
};
|
||||
|
||||
// Pick first three
|
||||
pick( marketing, ! isMarketingTaskCompleted );
|
||||
pick( payments, ! isPaymentsTaskCompleted );
|
||||
pick( extensions, true ); // No condition yet
|
||||
|
||||
// Pick second three
|
||||
pick( mobileApp, isMobileTaskCompleted );
|
||||
pick( mailchimp, true ); // No condition yet
|
||||
pick( externalDocumentation, true ); // No condition yet
|
||||
|
||||
// Pick last three
|
||||
pick( payments, true );
|
||||
pick( extensions, true );
|
||||
pick( externalDocumentation, true );
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
export const WhatsNext = ( {
|
||||
activePlugins,
|
||||
allTasklists,
|
||||
}: WhatsNextProps ) => {
|
||||
const actions = useMemo( () => {
|
||||
return getActionsList( { activePlugins, allTasklists } );
|
||||
}, [ activePlugins, allTasklists ] );
|
||||
|
||||
return (
|
||||
<div className="woocommerce-launch-store__congrats-main-actions">
|
||||
{ actions.map( ( item, index ) => (
|
||||
<div
|
||||
className="woocommerce-launch-store__congrats-action"
|
||||
key={ index }
|
||||
>
|
||||
<div className="woocommerce-launch-store__congrats-action__content">
|
||||
<h3>{ item.title }</h3>
|
||||
<p>{ item.description }</p>
|
||||
<Button
|
||||
variant="link"
|
||||
href={ item.link }
|
||||
target={
|
||||
item.link.indexOf( ADMIN_URL ) === -1
|
||||
? '_blank'
|
||||
: '_self'
|
||||
}
|
||||
onClick={ () => {
|
||||
recordEvent( item.trackEvent );
|
||||
} }
|
||||
>
|
||||
{ item.linkText }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) ) }
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { DoneActorEvent } from 'xstate5';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
import { OPTIONS_STORE_NAME, TaskListType } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { MainContentMachineContext } from '../../../main-content/xstate';
|
||||
|
||||
export const assignCompleteSurvey = {
|
||||
congratsScreen: ( { context }: { context: MainContentMachineContext } ) => {
|
||||
dispatch( OPTIONS_STORE_NAME ).updateOptions( {
|
||||
woocommerce_admin_launch_your_store_survey_completed: 'yes',
|
||||
} );
|
||||
|
||||
return {
|
||||
...context.congratsScreen,
|
||||
hasCompleteSurvey: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const assignCongratsData = {
|
||||
congratsScreen: ( {
|
||||
context,
|
||||
event,
|
||||
}: {
|
||||
context: MainContentMachineContext;
|
||||
event: DoneActorEvent< {
|
||||
surveyCompleted: string | null;
|
||||
tasklists: TaskListType[];
|
||||
activePlugins: string[];
|
||||
} >;
|
||||
} ) => {
|
||||
return {
|
||||
...context.congratsScreen,
|
||||
hasLoadedCongratsData: true,
|
||||
hasCompleteSurvey: event.output.surveyCompleted === 'yes',
|
||||
allTasklists: event.output.tasklists,
|
||||
activePlugins: event.output.activePlugins,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { Spinner } from '@woocommerce/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { MainContentComponentProps } from '../../xstate';
|
||||
import { Congrats } from './Congrats';
|
||||
export * as actions from './actions';
|
||||
export * as services from './services';
|
||||
export type events = { type: 'COMPLETE_SURVEY' };
|
||||
import { WhatsNext } from './WhatsNext';
|
||||
import { isWooExpress } from '~/utils/is-woo-express';
|
||||
|
||||
export const LaunchYourStoreSuccess = ( props: MainContentComponentProps ) => {
|
||||
const completeSurvey = () => {
|
||||
props.sendEventToMainContent( { type: 'COMPLETE_SURVEY' } );
|
||||
};
|
||||
|
||||
// Temporary spinner until data load is moved to loading screen or somewhere else.
|
||||
if ( ! props.context.congratsScreen.hasLoadedCongratsData ) {
|
||||
return (
|
||||
<div className="spinner-container">
|
||||
<Spinner></Spinner>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
'launch-store-success-page__container',
|
||||
props.className
|
||||
) }
|
||||
>
|
||||
<Congrats
|
||||
hasCompleteSurvey={
|
||||
props.context.congratsScreen.hasCompleteSurvey
|
||||
}
|
||||
isWooExpress={ isWooExpress() }
|
||||
completeSurvey={ completeSurvey }
|
||||
>
|
||||
<h2 className="woocommerce-launch-store__congrats-main-actions-title">
|
||||
{ __( "What's next?", 'woocommerce' ) }
|
||||
</h2>
|
||||
<WhatsNext
|
||||
activePlugins={ props.context.congratsScreen.activePlugins }
|
||||
allTasklists={ props.context.congratsScreen.allTasklists }
|
||||
/>
|
||||
</Congrats>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
ONBOARDING_STORE_NAME,
|
||||
OPTIONS_STORE_NAME,
|
||||
PLUGINS_STORE_NAME,
|
||||
} from '@woocommerce/data';
|
||||
import { resolveSelect } from '@wordpress/data';
|
||||
import { fromPromise } from 'xstate5';
|
||||
|
||||
export const fetchCongratsData = fromPromise( async () => {
|
||||
const [ surveyCompleted, tasklists, activePlugins ] = await Promise.all( [
|
||||
resolveSelect( OPTIONS_STORE_NAME ).getOption(
|
||||
'woocommerce_admin_launch_your_store_survey_completed'
|
||||
),
|
||||
resolveSelect( ONBOARDING_STORE_NAME ).getTaskListsByIds( [
|
||||
'setup',
|
||||
'extended',
|
||||
] ),
|
||||
resolveSelect( PLUGINS_STORE_NAME ).getActivePlugins(),
|
||||
] );
|
||||
return {
|
||||
surveyCompleted: surveyCompleted as string | null,
|
||||
tasklists,
|
||||
activePlugins,
|
||||
};
|
||||
} );
|
|
@ -0,0 +1,292 @@
|
|||
.launch-store-success-page__container {
|
||||
background: #fff;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 0;
|
||||
}
|
||||
|
||||
.launch-your-store-layout__content {
|
||||
.spinner-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
.woocommerce-launch-store__congrats-header-container {
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
|
||||
.woologo {
|
||||
padding-top: 8px;
|
||||
|
||||
svg {
|
||||
margin-left: 38px;
|
||||
margin-right: 24px;
|
||||
width: 38px;
|
||||
height: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.back-to-home-button {
|
||||
padding: 0;
|
||||
height: 24px;
|
||||
text-decoration: none;
|
||||
|
||||
.dashicon {
|
||||
padding-right: 22px;
|
||||
}
|
||||
span {
|
||||
color: #3c434a;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
|
||||
.woocommerce-launch-store__congrats-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-heading {
|
||||
color: $gray-900;
|
||||
text-align: center;
|
||||
font-feature-settings: "clig" off, "liga" off;
|
||||
font-size: 32px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 60px; /* 187.5% */
|
||||
letter-spacing: -0.32px;
|
||||
margin: 0;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
line-height: 38px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-subheading {
|
||||
color: $gray-700;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.1px;
|
||||
margin: 4px 0 0;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-main-actions-title {
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 120% */
|
||||
margin-top: 50px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-main-actions {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
flex-direction: row;
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
letter-spacing: -0.32px;
|
||||
}
|
||||
|
||||
.components-button {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-action {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-action__content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-action__content {
|
||||
max-width: 250px;
|
||||
padding-right: 2px;
|
||||
|
||||
p {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-midsection-container {
|
||||
margin: 50px 0;
|
||||
border: 1px solid #dcdcde;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
max-width: 100%;
|
||||
width: 650px;
|
||||
|
||||
.woocommerce-launch-store__congrats-thanks {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 4px 0;
|
||||
|
||||
.thanks-copy {
|
||||
flex-grow: 1;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-section_1 {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-section_2 {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-visit-store {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.buttons-container {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.store-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
flex-grow: 1;
|
||||
width: 353px;
|
||||
}
|
||||
|
||||
.buttons-container {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
hr.separator {
|
||||
margin: 0;
|
||||
border-top: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-survey {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 24px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-survey__selection {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.customer-feedback-simple__container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
|
||||
p {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
.components-button {
|
||||
box-sizing: border-box;
|
||||
height: 30px;
|
||||
|
||||
&.is-selected,
|
||||
&:hover {
|
||||
background-color: rgba(var(--wp-admin-theme-color--rgb), 0.1);
|
||||
outline: 3px solid transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-launch-store__congrats-survey__comment {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
color: rgb(30, 30, 30);
|
||||
|
||||
.comment-label {
|
||||
display: block;
|
||||
|
||||
.small-text {
|
||||
color: #757575;
|
||||
}
|
||||
}
|
||||
|
||||
.privacy-text {
|
||||
font-size: 11px;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.components-base-control .components-base-control__field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,32 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { fromCallback, setup } from 'xstate5';
|
||||
import { assign, fromCallback, setup } from 'xstate5';
|
||||
import React from 'react';
|
||||
import { getQuery } from '@woocommerce/navigation';
|
||||
import type { TaskListType } from '@woocommerce/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { LoadingPage } from './pages/loading';
|
||||
import { LaunchYourStoreSuccess } from './pages/launch-store-success';
|
||||
import { SitePreviewPage } from './pages/site-preview';
|
||||
import type { LaunchYourStoreComponentProps } from '..';
|
||||
import { createQueryParamsListener, updateQueryParams } from '../common';
|
||||
import {
|
||||
services as congratsServices,
|
||||
events as congratsEvents,
|
||||
actions as congratsActions,
|
||||
LaunchYourStoreSuccess,
|
||||
} from './pages/launch-store-success';
|
||||
|
||||
export type MainContentMachineContext = {
|
||||
placeholder?: string; // remove this when we have some types to put here
|
||||
congratsScreen: {
|
||||
hasLoadedCongratsData: boolean;
|
||||
hasCompleteSurvey: boolean;
|
||||
allTasklists: TaskListType[];
|
||||
activePlugins: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type MainContentComponentProps = LaunchYourStoreComponentProps & {
|
||||
|
@ -23,11 +34,14 @@ export type MainContentComponentProps = LaunchYourStoreComponentProps & {
|
|||
};
|
||||
export type MainContentMachineEvents =
|
||||
| { type: 'SHOW_LAUNCH_STORE_SUCCESS' }
|
||||
| { type: 'SHOW_LOADING' };
|
||||
| { type: 'EXTERNAL_URL_UPDATE' }
|
||||
| { type: 'SHOW_LOADING' }
|
||||
| congratsEvents;
|
||||
|
||||
const contentQueryParamListener = fromCallback( ( { sendBack } ) => {
|
||||
return createQueryParamsListener( 'content', sendBack );
|
||||
} );
|
||||
|
||||
export const mainContentMachine = setup( {
|
||||
types: {} as {
|
||||
context: MainContentMachineContext;
|
||||
|
@ -52,11 +66,23 @@ export const mainContentMachine = setup( {
|
|||
},
|
||||
actors: {
|
||||
contentQueryParamListener,
|
||||
fetchCongratsData: congratsServices.fetchCongratsData,
|
||||
},
|
||||
} ).createMachine( {
|
||||
id: 'mainContent',
|
||||
initial: 'navigate',
|
||||
context: {},
|
||||
context: {
|
||||
congratsScreen: {
|
||||
hasLoadedCongratsData: false,
|
||||
hasCompleteSurvey: false,
|
||||
allTasklists: [],
|
||||
activePlugins: [],
|
||||
},
|
||||
},
|
||||
invoke: {
|
||||
id: 'contentQueryParamListener',
|
||||
src: 'contentQueryParamListener',
|
||||
},
|
||||
states: {
|
||||
navigate: {
|
||||
always: [
|
||||
|
@ -86,6 +112,14 @@ export const mainContentMachine = setup( {
|
|||
},
|
||||
launchStoreSuccess: {
|
||||
id: 'launchStoreSuccess',
|
||||
invoke: [
|
||||
{
|
||||
src: 'fetchCongratsData',
|
||||
onDone: {
|
||||
actions: assign( congratsActions.assignCongratsData ),
|
||||
},
|
||||
},
|
||||
],
|
||||
entry: [
|
||||
{
|
||||
type: 'updateQueryParams',
|
||||
|
@ -95,6 +129,11 @@ export const mainContentMachine = setup( {
|
|||
meta: {
|
||||
component: LaunchYourStoreSuccess,
|
||||
},
|
||||
on: {
|
||||
COMPLETE_SURVEY: {
|
||||
actions: assign( congratsActions.assignCompleteSurvey ),
|
||||
},
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
id: 'loading',
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add Launch Your Store success screen
|
|
@ -220,6 +220,7 @@ class Options extends \WC_REST_Data_Controller {
|
|||
'woocommerce_admin_customize_store_completed',
|
||||
'woocommerce_admin_customize_store_completed_theme_id',
|
||||
'woocommerce_admin_customize_store_survey_completed',
|
||||
'woocommerce_admin_launch_your_store_survey_completed',
|
||||
'woocommerce_coming_soon',
|
||||
'woocommerce_store_pages_only',
|
||||
'woocommerce_private_link',
|
||||
|
|
|
@ -716,6 +716,9 @@ importers:
|
|||
'@wordpress/viewport':
|
||||
specifier: ^4.20.0
|
||||
version: 4.20.0(react@17.0.2)
|
||||
canvas-confetti:
|
||||
specifier: ^1.9.2
|
||||
version: 1.9.2
|
||||
classnames:
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
|
@ -837,6 +840,9 @@ importers:
|
|||
'@testing-library/user-event':
|
||||
specifier: 13.5.0
|
||||
version: 13.5.0(@testing-library/dom@8.11.3)
|
||||
'@types/canvas-confetti':
|
||||
specifier: ^1.6.4
|
||||
version: 1.6.4
|
||||
'@types/jest':
|
||||
specifier: ^27.5.2
|
||||
version: 27.5.2
|
||||
|
@ -18823,6 +18829,10 @@ packages:
|
|||
'@types/node': 16.18.68
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
/@types/canvas-confetti@1.6.4:
|
||||
resolution: {integrity: sha512-fNyZ/Fdw/Y92X0vv7B+BD6ysHL4xVU5dJcgzgxLdGbn8O3PezZNIJpml44lKM0nsGur+o/6+NZbZeNTt00U1uA==}
|
||||
dev: true
|
||||
|
||||
/@types/cheerio@0.22.35:
|
||||
resolution: {integrity: sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==}
|
||||
dependencies:
|
||||
|
@ -27989,6 +27999,10 @@ packages:
|
|||
/caniuse-lite@1.0.30001568:
|
||||
resolution: {integrity: sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==}
|
||||
|
||||
/canvas-confetti@1.9.2:
|
||||
resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==}
|
||||
dev: false
|
||||
|
||||
/capital-case@1.0.4:
|
||||
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in New Issue