woocommerce/plugins/woocommerce-admin/client/payments/payment-recommendations.tsx

254 lines
7.0 KiB
TypeScript
Raw Normal View History

/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { decodeEntities } from '@wordpress/html-entities';
import { Card, CardHeader, CardFooter, Button } from '@wordpress/components';
import { useEffect, useRef, useState } from '@wordpress/element';
import { EllipsisMenu, List, Pill } from '@woocommerce/components';
import { Text } from '@woocommerce/experimental';
import {
PLUGINS_STORE_NAME,
SETTINGS_STORE_NAME,
WCDataSelector,
Plugin,
WPDataSelectors,
OPTIONS_STORE_NAME,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import ExternalIcon from 'gridicons/dist/external';
Allow packages to be built in isolation. (https://github.com/woocommerce/woocommerce-admin/pull/7286) * Use yarn instead of npm. In prep for workspaces, since we're locked to npm < 7. See: https://github.com/woocommerce/woocommerce-admin/pull/7126#issue-661287749 * Initial workspace creation. * Add initial tsc build to @woocommerce/number. * Attempt to build experimental package. * Try currency package. * Define all packages as workspaces. * Use tsconfig common to packages. * Fix currency package build. * Build csv-export with tsc. * Try to build customer-effort-score with tsc. * Fix JSX pragma. * Build data package with tsc. * Build date package with tsc. * Build experimental package with tsc. * Try to build explat package with tsc. * Build navigation package with tsc. * Build notices package with tsc. * Build onboarding package with tsc. * Build components package with tsc. * Swap in package JS build into main script. * Fix experimental package build. * Try per-package css build with components. * Try to run components package tests in isolation. Broken on JSX in test files not being transformed. * Move @woocommerce/wc-admin-settings into a package. * Try to fix components package tests. Fails because we aren't setting up the jest/jest-dom globals. * Move JS test code to reusable (private) package. * Enable incremental TS builds. * Use workspaces to run JS tests. * Use new jest configs for update snapshot scripts. * Fix style builds. * Fix package version in components. * Fix client test debug and watch scripts. * Update yarn lock. * Update test-staged behavior. * Try to fix storybook. * Fix storybook. * Update more npm commands to yarn. * Add changelog. * Fix lint errors. * Update packages readme script references. * Clean up unused gitignore match. * Fix another npm command. * Fix JS builds on watch. * Fix start script. * Fix start scripts for packages. * Use tsc to build packages before tests * yarn -> npm. # Conflicts: # package-lock.json # package.json * Fix linter error. * Remove workspace definitions. * Fix missing Fragment import. * Fix package lock. * Fix missing reference. * Only build commonjs module for js-tests helper. * Remove errant dependency from components. * Remove noop scripts. * Fix package JS build before testing. * Revert noisy formatting changes. * Fix precommit and test scripts. * Fix minimum expected recommended extension count. Japan test case breaks this. * Revert babel config changes. * chore(release): publish - @woocommerce/components@7.2.0 - @woocommerce/csv-export@1.4.0 - @woocommerce/currency@3.2.0 - @woocommerce/customer-effort-score@1.1.0 - @woocommerce/data@1.4.0 - @woocommerce/date@3.1.0 - @woocommerce/dependency-extraction-webpack-plugin@1.7.0 - @woocommerce/eslint-plugin@1.3.0 - @woocommerce/experimental@1.5.0 - @woocommerce/explat@1.1.0 - @woocommerce/js-tests@1.1.0 - @woocommerce/navigation@6.1.0 - @woocommerce/notices@3.1.0 - @woocommerce/number@2.2.0 - @woocommerce/onboarding@1.1.0 - @woocommerce/tracks@1.1.0 - @woocommerce/wc-admin-settings@1.1.0 * Add script for running 'start' in a package. * Remove yarn from gitignore. * Update package changelogs, prep versions for release. * Try to fix E2E tests after main merge. * Some cleanup. * Add changelog. Co-authored-by: Paul Sealock <psealock@gmail.com>
2021-07-14 20:38:57 +00:00
import { getAdminLink } from '@woocommerce/wc-admin-settings';
/**
* Internal dependencies
*/
import './payment-recommendations.scss';
import { getCountryCode } from '../dashboard/utils';
import { createNoticesFromResponse } from '../lib/notices';
import { isWCPaySupported } from '~/tasks/fills/PaymentGatewaySuggestions/components/WCPay';
const SEE_MORE_LINK =
'https://woocommerce.com/product-category/woocommerce-extensions/payment-gateways/?utm_source=payments_recommendations';
const DISMISS_OPTION = 'woocommerce_setting_payments_recommendations_hidden';
type SettingsSelector = WPDataSelectors & {
getSettings: (
type: string
) => { general: { woocommerce_default_country?: string } };
};
type OptionsSelector = WPDataSelectors & {
getOption: ( option: string ) => boolean | string;
};
export function getPaymentRecommendationData(
select: WCDataSelector
): {
displayable: boolean;
recommendedPlugins?: Plugin[];
isLoading: boolean;
} {
const { getOption, isResolving: isResolvingOption } = select(
OPTIONS_STORE_NAME
) as OptionsSelector;
const { getSettings } = select( SETTINGS_STORE_NAME ) as SettingsSelector;
const { getRecommendedPlugins } = select( PLUGINS_STORE_NAME );
const { general: settings } = getSettings( 'general' );
const hidden = getOption( DISMISS_OPTION );
const countryCode =
settings && settings.woocommerce_default_country
? getCountryCode( settings.woocommerce_default_country )
: null;
const countrySupported = countryCode
? isWCPaySupported( countryCode )
: false;
const isRequestingOptions = isResolvingOption( 'getOption', [
DISMISS_OPTION,
] );
const displayable =
! isRequestingOptions && hidden !== 'yes' && countrySupported;
let plugins = null;
if ( displayable ) {
// don't get recommended plugins until it is displayable.
plugins = getRecommendedPlugins( 'payments' );
}
const isLoading =
isRequestingOptions ||
hidden === undefined ||
settings === undefined ||
plugins === undefined;
return {
displayable,
recommendedPlugins: plugins,
isLoading,
};
}
const WcPayPromotionGateway = document.querySelector(
'[data-gateway_id="pre_install_woocommerce_payments_promotion"]'
);
const PaymentRecommendations: React.FC = () => {
const [ installingPlugin, setInstallingPlugin ] = useState< string | null >(
null
);
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME );
const { displayable, recommendedPlugins, isLoading } = useSelect(
getPaymentRecommendationData
);
const triggeredPageViewRef = useRef( false );
const shouldShowRecommendations =
displayable && recommendedPlugins && recommendedPlugins.length > 0;
useEffect( () => {
if (
( shouldShowRecommendations ||
( WcPayPromotionGateway && ! isLoading ) ) &&
! triggeredPageViewRef.current
) {
triggeredPageViewRef.current = true;
const eventProps = ( recommendedPlugins || [] ).reduce(
( props, plugin ) => {
if ( plugin.product ) {
return {
...props,
[ plugin.product.replace( /\-/g, '_' ) +
'_displayed' ]: true,
};
}
return props;
},
{
woocommerce_payments_displayed: !! WcPayPromotionGateway,
}
);
recordEvent(
'settings_payments_recommendations_pageview',
eventProps
);
}
}, [ shouldShowRecommendations, WcPayPromotionGateway, isLoading ] );
if ( ! shouldShowRecommendations ) {
return null;
}
const dismissPaymentRecommendations = () => {
recordEvent( 'settings_payments_recommendations_dismiss', {} );
updateOptions( {
[ DISMISS_OPTION ]: 'yes',
} );
};
const setupPlugin = ( plugin: Plugin ) => {
if ( installingPlugin ) {
return;
}
setInstallingPlugin( plugin.product );
recordEvent( 'settings_payments_recommendations_setup', {
extension_selected: plugin.product,
} );
installAndActivatePlugins( [ plugin.product ] )
.then( () => {
window.location.href = getAdminLink(
plugin[ 'setup-link' ].replace( '/wp-admin/', '' )
);
} )
.catch( ( response: { errors: Record< string, string > } ) => {
createNoticesFromResponse( response );
setInstallingPlugin( null );
} );
};
const pluginsList = ( recommendedPlugins || [] ).map(
( plugin: Plugin ) => {
return {
key: plugin.slug,
title: (
<>
{ plugin.title }
{ plugin.recommended && (
<Pill>
{ __( 'Recommended', 'woocommerce-admin' ) }
</Pill>
) }
</>
),
content: decodeEntities( plugin.copy ),
after: (
<Button
isSecondary
onClick={ () => setupPlugin( plugin ) }
isBusy={ installingPlugin === plugin.product }
disabled={ !! installingPlugin }
>
{ plugin[ 'button-text' ] }
</Button>
),
before: <img src={ plugin.icon } alt="" />,
};
}
);
return (
<Card size="medium" className="woocommerce-recommended-payments-card">
<CardHeader>
<div className="woocommerce-recommended-payments-card__header">
<Text
variant="title.small"
as="p"
size="20"
lineHeight="28px"
>
{ __(
'Recommended ways to get paid',
'woocommerce-admin'
) }
</Text>
<Text
className={
'woocommerce-recommended-payments__header-heading'
}
variant="caption"
as="p"
size="12"
lineHeight="16px"
>
{ __(
'We recommend adding one of the following payment extensions to your store. The extension will be installed and activated for you when you click "Get started".',
'woocommerce-admin'
) }
</Text>
</div>
<div className="woocommerce-card__menu woocommerce-card__header-item">
<EllipsisMenu
label={ __( 'Task List Options', 'woocommerce-admin' ) }
renderContent={ () => (
<div className="woocommerce-review-activity-card__section-controls">
<Button
onClick={ dismissPaymentRecommendations }
>
{ __( 'Hide this', 'woocommerce-admin' ) }
</Button>
</div>
) }
/>
</div>
</CardHeader>
<List items={ pluginsList } />
<CardFooter>
<Button href={ SEE_MORE_LINK } target="_blank" isTertiary>
{ __( 'See more options', 'woocommerce-admin' ) }
<ExternalIcon size={ 18 } />
</Button>
</CardFooter>
</Card>
);
};
export default PaymentRecommendations;