2023-05-29 14:45:30 +00:00
|
|
|
|
/**
|
|
|
|
|
* External dependencies
|
|
|
|
|
*/
|
|
|
|
|
import { __, sprintf, _n } from '@wordpress/i18n';
|
|
|
|
|
import { Button } from '@wordpress/components';
|
|
|
|
|
import interpolateComponents from '@automattic/interpolate-components';
|
|
|
|
|
import { Link } from '@woocommerce/components';
|
|
|
|
|
import { Extension, ExtensionList } from '@woocommerce/data';
|
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Internal dependencies
|
|
|
|
|
*/
|
2024-06-07 07:06:40 +00:00
|
|
|
|
import { CoreProfilerStateMachineContext } from '../index';
|
2023-06-13 22:03:03 +00:00
|
|
|
|
import {
|
2024-06-07 07:06:40 +00:00
|
|
|
|
PluginsLearnMoreLinkClickedEvent,
|
|
|
|
|
PluginsInstallationRequestedEvent,
|
|
|
|
|
PluginsPageSkippedEvent,
|
|
|
|
|
} from '../events';
|
2023-05-29 14:45:30 +00:00
|
|
|
|
import { Heading } from '../components/heading/heading';
|
|
|
|
|
import { Navigation } from '../components/navigation/navigation';
|
|
|
|
|
import { PluginCard } from '../components/plugin-card/plugin-card';
|
|
|
|
|
import { getAdminSetting } from '~/utils/admin-settings';
|
|
|
|
|
|
|
|
|
|
const locale = ( getAdminSetting( 'locale' )?.siteLocale || 'en_US' ).replace(
|
|
|
|
|
'_',
|
|
|
|
|
'-'
|
|
|
|
|
);
|
|
|
|
|
const joinWithAnd = ( items: string[] ) => {
|
|
|
|
|
return new Intl.ListFormat( locale, {
|
|
|
|
|
style: 'long',
|
|
|
|
|
type: 'conjunction',
|
|
|
|
|
} ).formatToParts( items );
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const Plugins = ( {
|
|
|
|
|
context,
|
|
|
|
|
navigationProgress,
|
|
|
|
|
sendEvent,
|
|
|
|
|
}: {
|
2023-07-24 12:34:38 +00:00
|
|
|
|
context: Pick<
|
|
|
|
|
CoreProfilerStateMachineContext,
|
|
|
|
|
'pluginsAvailable' | 'pluginsInstallationErrors' | 'pluginsSelected'
|
|
|
|
|
>;
|
2023-05-29 14:45:30 +00:00
|
|
|
|
sendEvent: (
|
2023-06-13 22:03:03 +00:00
|
|
|
|
payload:
|
|
|
|
|
| PluginsInstallationRequestedEvent
|
|
|
|
|
| PluginsPageSkippedEvent
|
2024-06-07 07:06:40 +00:00
|
|
|
|
| PluginsLearnMoreLinkClickedEvent
|
2023-05-29 14:45:30 +00:00
|
|
|
|
) => void;
|
|
|
|
|
navigationProgress: number;
|
|
|
|
|
} ) => {
|
|
|
|
|
const [ selectedPlugins, setSelectedPlugins ] = useState<
|
|
|
|
|
ExtensionList[ 'plugins' ]
|
2023-06-27 00:31:16 +00:00
|
|
|
|
>(
|
|
|
|
|
context.pluginsAvailable.filter(
|
|
|
|
|
context.pluginsInstallationErrors.length
|
|
|
|
|
? ( plugin ) => context.pluginsSelected.includes( plugin.key )
|
|
|
|
|
: ( plugin ) => ! plugin.is_activated
|
|
|
|
|
)
|
|
|
|
|
);
|
2023-05-29 14:45:30 +00:00
|
|
|
|
|
|
|
|
|
const setSelectedPlugin = ( plugin: Extension ) => {
|
|
|
|
|
setSelectedPlugins(
|
|
|
|
|
selectedPlugins.some( ( item ) => item.key === plugin.key )
|
|
|
|
|
? selectedPlugins.filter( ( item ) => item.key !== plugin.key )
|
|
|
|
|
: [ ...selectedPlugins, plugin ]
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const skipPluginsPage = () => {
|
|
|
|
|
return sendEvent( {
|
|
|
|
|
type: 'PLUGINS_PAGE_SKIPPED',
|
|
|
|
|
} );
|
|
|
|
|
};
|
2023-08-17 14:34:52 +00:00
|
|
|
|
|
2023-05-29 14:45:30 +00:00
|
|
|
|
const submitInstallationRequest = () => {
|
2023-08-17 14:34:52 +00:00
|
|
|
|
const selectedPluginSlugs = selectedPlugins.map( ( plugin ) =>
|
|
|
|
|
plugin.key.replace( ':alt', '' )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const pluginsShown: string[] = [];
|
|
|
|
|
const pluginsUnselected: string[] = [];
|
|
|
|
|
|
|
|
|
|
context.pluginsAvailable.forEach( ( plugin ) => {
|
|
|
|
|
const pluginSlug = plugin.key.replace( ':alt', '' );
|
|
|
|
|
pluginsShown.push( pluginSlug );
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
! plugin.is_activated &&
|
|
|
|
|
! selectedPluginSlugs.includes( pluginSlug )
|
|
|
|
|
) {
|
|
|
|
|
pluginsUnselected.push( pluginSlug );
|
|
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
|
2023-05-29 14:45:30 +00:00
|
|
|
|
return sendEvent( {
|
|
|
|
|
type: 'PLUGINS_INSTALLATION_REQUESTED',
|
|
|
|
|
payload: {
|
2023-08-17 14:34:52 +00:00
|
|
|
|
pluginsShown,
|
|
|
|
|
pluginsSelected: selectedPluginSlugs,
|
|
|
|
|
pluginsUnselected,
|
2023-05-29 14:45:30 +00:00
|
|
|
|
},
|
|
|
|
|
} );
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const composeListFormatParts = ( part: {
|
|
|
|
|
type: string;
|
|
|
|
|
value: string;
|
|
|
|
|
} ) => {
|
|
|
|
|
if ( part.type === 'element' ) {
|
|
|
|
|
return '{{span}}' + part.value + '{{/span}}';
|
|
|
|
|
}
|
|
|
|
|
return part.value;
|
|
|
|
|
};
|
|
|
|
|
const errorMessage = context.pluginsInstallationErrors.length
|
|
|
|
|
? interpolateComponents( {
|
|
|
|
|
mixedString: sprintf(
|
|
|
|
|
// Translators: %s is a list of plugins that does not need to be translated
|
|
|
|
|
__(
|
|
|
|
|
'Oops! We encountered a problem while installing %s. {{link}}Please try again{{/link}}.',
|
|
|
|
|
'woocommerce'
|
|
|
|
|
),
|
|
|
|
|
joinWithAnd(
|
|
|
|
|
context.pluginsInstallationErrors.map(
|
|
|
|
|
( error ) => error.plugin
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.map( composeListFormatParts )
|
|
|
|
|
.join( '' )
|
|
|
|
|
),
|
|
|
|
|
components: {
|
|
|
|
|
span: <span />,
|
|
|
|
|
link: (
|
|
|
|
|
<Button isLink onClick={ submitInstallationRequest } />
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
} )
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
const pluginsWithAgreement = selectedPlugins.filter( ( plugin ) =>
|
|
|
|
|
[
|
|
|
|
|
'jetpack',
|
|
|
|
|
'woocommerce-services:shipping',
|
|
|
|
|
'woocommerce-services:tax',
|
|
|
|
|
].includes( plugin.key )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className="woocommerce-profiler-plugins"
|
|
|
|
|
data-testid="core-profiler-plugins"
|
|
|
|
|
>
|
|
|
|
|
<Navigation
|
|
|
|
|
percentage={ navigationProgress }
|
|
|
|
|
onSkip={ skipPluginsPage }
|
|
|
|
|
/>
|
|
|
|
|
<div className="woocommerce-profiler-page__content woocommerce-profiler-plugins__content">
|
|
|
|
|
<Heading
|
|
|
|
|
className="woocommerce-profiler__stepper-heading"
|
|
|
|
|
title={ __(
|
|
|
|
|
'Get a boost with our free features',
|
|
|
|
|
'woocommerce'
|
|
|
|
|
) }
|
|
|
|
|
subTitle={ __(
|
|
|
|
|
'Enhance your store by installing these free business features. No commitment required – you can remove them at any time.',
|
|
|
|
|
'woocommerce'
|
|
|
|
|
) }
|
|
|
|
|
/>
|
|
|
|
|
{ errorMessage && (
|
|
|
|
|
<p className="plugin-error">{ errorMessage }</p>
|
|
|
|
|
) }
|
|
|
|
|
<div className="woocommerce-profiler-plugins__list">
|
|
|
|
|
{ context.pluginsAvailable.map( ( plugin ) => {
|
2023-06-13 22:03:03 +00:00
|
|
|
|
const learnMoreLink = plugin.learn_more_link ? (
|
|
|
|
|
<Link
|
2024-08-01 05:26:05 +00:00
|
|
|
|
onClick={ ( e ) => {
|
2023-06-13 22:03:03 +00:00
|
|
|
|
sendEvent( {
|
|
|
|
|
type: 'PLUGINS_LEARN_MORE_LINK_CLICKED',
|
|
|
|
|
payload: {
|
|
|
|
|
plugin: plugin.key,
|
|
|
|
|
learnMoreLink:
|
|
|
|
|
plugin.learn_more_link ?? '',
|
|
|
|
|
},
|
|
|
|
|
} );
|
2024-08-01 05:26:05 +00:00
|
|
|
|
e.stopPropagation();
|
2023-06-13 22:03:03 +00:00
|
|
|
|
} }
|
|
|
|
|
href={ plugin.learn_more_link }
|
|
|
|
|
target="_blank"
|
|
|
|
|
type="external"
|
|
|
|
|
>
|
|
|
|
|
{ __( 'Learn More', 'woocommerce' ) }
|
|
|
|
|
</Link>
|
|
|
|
|
) : null;
|
2023-05-29 14:45:30 +00:00
|
|
|
|
return (
|
|
|
|
|
<PluginCard
|
|
|
|
|
key={ `checkbox-control-${ plugin.key }` }
|
2023-06-13 22:03:03 +00:00
|
|
|
|
installed={ plugin.is_activated }
|
2023-05-29 14:45:30 +00:00
|
|
|
|
onChange={ () => {
|
|
|
|
|
setSelectedPlugin( plugin );
|
|
|
|
|
} }
|
|
|
|
|
checked={
|
|
|
|
|
selectedPlugins.filter(
|
|
|
|
|
( item ) => item.key === plugin.key
|
|
|
|
|
).length > 0
|
|
|
|
|
}
|
|
|
|
|
icon={
|
|
|
|
|
plugin.image_url ? (
|
|
|
|
|
<img
|
|
|
|
|
src={ plugin.image_url }
|
|
|
|
|
alt={ plugin.key }
|
|
|
|
|
/>
|
|
|
|
|
) : null
|
|
|
|
|
}
|
2023-06-13 22:03:03 +00:00
|
|
|
|
title={ plugin.label }
|
2023-05-29 14:45:30 +00:00
|
|
|
|
description={ plugin.description }
|
2023-06-13 22:03:03 +00:00
|
|
|
|
learnMoreLink={ learnMoreLink }
|
2023-05-29 14:45:30 +00:00
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
} ) }
|
|
|
|
|
</div>
|
|
|
|
|
<div className="woocommerce-profiler-plugins-continue-button-container">
|
|
|
|
|
<Button
|
|
|
|
|
className="woocommerce-profiler-plugins-continue-button"
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={
|
|
|
|
|
selectedPlugins.length
|
|
|
|
|
? submitInstallationRequest
|
|
|
|
|
: skipPluginsPage
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{ __( 'Continue', 'woocommerce' ) }
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
{ pluginsWithAgreement.length > 0 && (
|
|
|
|
|
<p className="woocommerce-profiler-plugins-jetpack-agreement">
|
|
|
|
|
{ interpolateComponents( {
|
|
|
|
|
mixedString: sprintf(
|
|
|
|
|
/* translators: %s: a list of plugins, e.g. Jetpack */
|
|
|
|
|
_n(
|
|
|
|
|
'By installing %s plugin for free you agree to our {{link}}Terms of Service{{/link}}.',
|
|
|
|
|
'By installing %s plugins for free you agree to our {{link}}Terms of Service{{/link}}.',
|
|
|
|
|
pluginsWithAgreement.length,
|
|
|
|
|
'woocommerce'
|
|
|
|
|
),
|
|
|
|
|
joinWithAnd(
|
|
|
|
|
pluginsWithAgreement.map(
|
|
|
|
|
( plugin ) => plugin.name
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.map( composeListFormatParts )
|
|
|
|
|
.join( '' )
|
|
|
|
|
),
|
|
|
|
|
components: {
|
|
|
|
|
span: <span />,
|
|
|
|
|
link: (
|
|
|
|
|
<Link
|
|
|
|
|
href="https://wordpress.com/tos/"
|
|
|
|
|
target="_blank"
|
|
|
|
|
type="external"
|
|
|
|
|
/>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
} ) }
|
|
|
|
|
</p>
|
|
|
|
|
) }
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|