diff --git a/plugins/woocommerce-admin/TESTING-INSTRUCTIONS.md b/plugins/woocommerce-admin/TESTING-INSTRUCTIONS.md index 99ff463b48d..4fdf20dee42 100644 --- a/plugins/woocommerce-admin/TESTING-INSTRUCTIONS.md +++ b/plugins/woocommerce-admin/TESTING-INSTRUCTIONS.md @@ -11,6 +11,15 @@ 5. Navigate to Homescreen. 6. Navigate back to previous Analytics Report. 7. Ensure that the time period is _still_ what you set on step 2. +### Refactor payments to allow management of methods #6786 + +1. Do not select "CBD industry" as a store industry during onboarding. +2. Make various payment methods visible by switching to different countries. +3. Attempt to set up various payment methods. +4. Make sure that after setup, a `Manage` link is shown that links to the payment method settings page. +5. Check that simple methods like, cash delivery or bank transfer initially have an `Enable` option. + +### Fix varation bug with Products reports #6647 ### Fix varation bug with Products reports #6647 diff --git a/plugins/woocommerce-admin/client/layout/index.js b/plugins/woocommerce-admin/client/layout/index.js index 3a5a4cec2f5..22efe9d487f 100644 --- a/plugins/woocommerce-admin/client/layout/index.js +++ b/plugins/woocommerce-admin/client/layout/index.js @@ -36,7 +36,7 @@ const StoreAlerts = lazy( () => const WCPayUsageModal = lazy( () => import( - /* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/wcpay/wcpay-usage-modal' + /* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/methods/wcpay/wcpay-usage-modal' ) ); diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/business-details/flows/selective-bundle/selective-extensions-bundle/index.js b/plugins/woocommerce-admin/client/profile-wizard/steps/business-details/flows/selective-bundle/selective-extensions-bundle/index.js index 11f29961b72..88ee7ef5472 100644 --- a/plugins/woocommerce-admin/client/profile-wizard/steps/business-details/flows/selective-bundle/selective-extensions-bundle/index.js +++ b/plugins/woocommerce-admin/client/profile-wizard/steps/business-details/flows/selective-bundle/selective-extensions-bundle/index.js @@ -22,7 +22,7 @@ import { AppIllustration } from '../app-illustration'; import './style.scss'; import { setAllPropsToValue } from '~/lib/collections'; import { getCountryCode } from '~/dashboard/utils'; -import { isWCPaySupported } from '~/task-list/tasks/payments/wcpay'; +import { isWCPaySupported } from '~/task-list/tasks/payments/methods/wcpay'; const generatePluginDescriptionWithLink = ( description, diff --git a/plugins/woocommerce-admin/client/task-list/tasks.js b/plugins/woocommerce-admin/client/task-list/tasks.js index b8ae7a91874..6c0b69f0b9d 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks.js +++ b/plugins/woocommerce-admin/client/task-list/tasks.js @@ -24,7 +24,7 @@ import Payments from './tasks/payments'; import { installActivateAndConnectWcpay, isWCPaySupported, -} from './tasks/payments/wcpay'; +} from './tasks/payments/methods/wcpay'; import { groupListOfObjectsBy } from '../lib/collections'; import { getLinkTypeAndHref } from '~/store-management-links'; diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/action.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/action.js new file mode 100644 index 00000000000..5302b74c507 --- /dev/null +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/action.js @@ -0,0 +1,98 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Button, Spinner } from '@wordpress/components'; +import { getAdminLink } from '@woocommerce/wc-admin-settings'; +import { updateQueryString } from '@woocommerce/navigation'; +import { useState } from '@wordpress/element'; + +export const Action = ( { + hasSetup = false, + isConfigured = false, + isEnabled = false, + isLoading = false, + isRecommended = false, + manageUrl = null, + markConfigured, + methodKey, + onSetUp = () => {}, + onSetupCallback, +} ) => { + const [ isBusy, setIsBusy ] = useState( false ); + + const classes = 'woocommerce-task-payment__action'; + + if ( isLoading ) { + return ; + } + + const handleClick = async () => { + onSetUp( methodKey ); + + if ( onSetupCallback ) { + setIsBusy( true ); + await new Promise( onSetupCallback ) + .then( () => { + setIsBusy( false ); + } ) + .catch( () => { + setIsBusy( false ); + } ); + + return; + } + + updateQueryString( { + method: methodKey, + } ); + }; + + if ( hasSetup && ! isConfigured ) { + return ( +
+ +
+ ); + } + + if ( ( hasSetup && isConfigured ) || ( ! hasSetup && isEnabled ) ) { + if ( ! manageUrl ) { + return null; + } + + return ( +
+ +
+ ); + } + + return ( + + ); +}; diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/index.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/index.js index 42d3a66e828..65121b56f50 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/index.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/index.js @@ -1,41 +1,28 @@ /** * External dependencies */ -import { __, sprintf } from '@wordpress/i18n'; import classnames from 'classnames'; -import { cloneElement, Component } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; -import { - Button, - Card, - CardBody, - CardMedia, - CardFooter, - FormToggle, - Spinner, -} from '@wordpress/components'; -import { withDispatch, withSelect } from '@wordpress/data'; -import { H, Plugins } from '@woocommerce/components'; -import { - getHistory, - getNewPath, - updateQueryString, -} from '@woocommerce/navigation'; +import { Card, CardBody, CardMedia, CardFooter } from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { H } from '@woocommerce/components'; +import { getHistory, getNewPath } from '@woocommerce/navigation'; import { ONBOARDING_STORE_NAME, OPTIONS_STORE_NAME, PLUGINS_STORE_NAME, - pluginNames, SETTINGS_STORE_NAME, } from '@woocommerce/data'; import { recordEvent } from '@woocommerce/tracks'; +import { useMemo, useState } from '@wordpress/element'; /** * Internal dependencies */ -import { createNoticesFromResponse } from '../../../lib/notices'; +import { Action } from './action'; import { getCountryCode } from '../../../dashboard/utils'; import { getPaymentMethods } from './methods'; +import { RecommendedRibbon } from './recommended-ribbon'; +import { Setup } from './setup'; export const setMethodEnabledOption = async ( optionName, @@ -57,331 +44,19 @@ export const setMethodEnabledOption = async ( } }; -class Payments extends Component { - constructor( props ) { - super( ...arguments ); - const { methods } = props; +export const Payments = ( { query } ) => { + const { createNotice } = useDispatch( 'core/notices' ); + const { + installAndActivatePlugins, + invalidateResolutionForStoreSelector: invalidatePluginStoreSelector, + } = useDispatch( PLUGINS_STORE_NAME ); + const { updateOptions } = useDispatch( OPTIONS_STORE_NAME ); + const { + invalidateResolution, + invalidateResolutionForStoreSelector, + } = useDispatch( ONBOARDING_STORE_NAME ); - const enabledMethods = {}; - methods.forEach( - ( method ) => ( enabledMethods[ method.key ] = method.isEnabled ) - ); - this.state = { - busyMethod: null, - enabledMethods, - recommendedMethod: this.getRecommendedMethod(), - }; - - this.markConfigured = this.markConfigured.bind( this ); - } - - componentDidUpdate() { - const { recommendedMethod } = this.state; - - const method = this.getRecommendedMethod(); - if ( recommendedMethod !== method ) { - this.setState( { - recommendedMethod: method, - } ); - } - } - - getRecommendedMethod() { - const { methods } = this.props; - const recommendedMethod = methods.find( - ( m ) => - ( m.key === 'wcpay' && m.visible ) || - ( m.key === 'mercadopago' && m.visible ) - ); - if ( ! recommendedMethod ) { - return 'stripe'; - } - return recommendedMethod.key; - } - - async markConfigured( methodName, queryParams = {} ) { - const { enabledMethods } = this.state; - const { methods } = this.props; - - const method = methods.find( ( option ) => option.key === methodName ); - - if ( ! method ) { - throw `Method ${ methodName } not found in available methods list`; - } - - this.setState( { - enabledMethods: { - ...enabledMethods, - [ methodName ]: true, - }, - } ); - - await setMethodEnabledOption( method.optionName, 'yes', this.props ); - - recordEvent( 'tasklist_payment_connect_method', { - payment_method: methodName, - } ); - - getHistory().push( - getNewPath( { ...queryParams, task: 'payments' }, '/', {} ) - ); - } - - getCurrentMethod() { - const { methods, query } = this.props; - - if ( ! query.method ) { - return; - } - - const currentMethod = methods.find( - ( method ) => method.key === query.method - ); - - if ( ! currentMethod ) { - throw `Current method ${ query.method } not found in available methods list`; - } - - return currentMethod; - } - - getInstallStep() { - const currentMethod = this.getCurrentMethod(); - - if ( ! currentMethod.plugins || ! currentMethod.plugins.length ) { - return; - } - - const { activePlugins } = this.props; - const pluginsToInstall = currentMethod.plugins.filter( - ( method ) => ! activePlugins.includes( method ) - ); - const pluginNamesString = currentMethod.plugins - .map( ( pluginSlug ) => pluginNames[ pluginSlug ] ) - .join( ' ' + __( 'and', 'woocommerce-admin' ) + ' ' ); - - return { - key: 'install', - label: sprintf( - __( 'Install %s', 'woocommerce-admin' ), - pluginNamesString - ), - content: ( - { - createNoticesFromResponse( response ); - recordEvent( 'tasklist_payment_install_method', { - plugins: currentMethod.plugins, - } ); - } } - onError={ ( errors, response ) => - createNoticesFromResponse( response ) - } - autoInstall - pluginSlugs={ currentMethod.plugins } - /> - ), - isComplete: ! pluginsToInstall.length, - }; - } - - async toggleMethod( key ) { - const { methods } = this.props; - const { enabledMethods } = this.state; - const method = methods.find( ( option ) => option.key === key ); - - if ( ! method ) { - throw `Method ${ key } not found in available methods list`; - } - - enabledMethods[ key ] = ! enabledMethods[ key ]; - this.setState( { enabledMethods } ); - - recordEvent( 'tasklist_payment_toggle', { - enabled: ! method.isEnabled, - payment_method: key, - } ); - - await setMethodEnabledOption( - method.optionName, - method.isEnabled ? 'no' : 'yes', - this.props - ); - } - - async handleClick( method ) { - const { methods } = this.props; - const { key, onClick } = method; - - recordEvent( 'tasklist_payment_setup', { - options: methods.map( ( option ) => option.key ), - selected: key, - } ); - - if ( onClick ) { - this.setState( { busyMethod: key } ); - await new Promise( onClick ) - .then( () => { - this.setState( { busyMethod: null } ); - } ) - .catch( () => { - this.setState( { busyMethod: null } ); - } ); - - return; - } - - updateQueryString( { - method: key, - } ); - } - - getSetupButtons( method ) { - const { busyMethod, enabledMethods, recommendedMethod } = this.state; - const { container, isConfigured, key } = method; - if ( container && ! isConfigured ) { - return ( -
- -
- ); - } - return ( - this.toggleMethod( key ) } - onClick={ ( e ) => e.stopPropagation() } - /> - ); - } - - render() { - const currentMethod = this.getCurrentMethod(); - const { recommendedMethod } = this.state; - const { methods, query } = this.props; - - if ( currentMethod ) { - return ( - - - { cloneElement( currentMethod.container, { - methodConfig: currentMethod, - query, - installStep: this.getInstallStep(), - markConfigured: this.markConfigured, - hasCbdIndustry: currentMethod.hasCbdIndustry, - } ) } - - - ); - } - - return ( -
- { methods.map( ( method ) => { - const { - before, - content, - isConfigured, - key, - title, - visible, - loading, - } = method; - - if ( ! visible ) { - return null; - } - - const classes = classnames( - 'woocommerce-task-payment', - 'woocommerce-task-card', - ! isConfigured && - 'woocommerce-task-payment-not-configured', - 'woocommerce-task-payment-' + key - ); - - const isRecommended = - key === recommendedMethod && ! isConfigured; - const showRecommendedRibbon = - isRecommended && key !== 'wcpay'; - const showRecommendedPill = - isRecommended && key === 'wcpay'; - const recommendedText = - key === 'mercadopago' - ? __( 'Local Partner', 'woocommerce-admin' ) - : __( 'Recommended', 'woocommerce-admin' ); - - return ( - - { showRecommendedRibbon && ( -
- { recommendedText } -
- ) } - { before } - - - { title } - { showRecommendedPill && ( - - { recommendedText } - - ) } - -
- { content } -
-
- - { loading ? ( - - ) : ( - this.getSetupButtons( method ) - ) } - -
- ); - } ) } -
- ); - } -} - -export default compose( - withDispatch( ( dispatch ) => { - const { createNotice } = dispatch( 'core/notices' ); - const { - installAndActivatePlugins, - invalidateResolutionForStoreSelector: invalidatePluginStoreSelector, - } = dispatch( PLUGINS_STORE_NAME ); - const { updateOptions } = dispatch( OPTIONS_STORE_NAME ); - const { - invalidateResolution, - invalidateResolutionForStoreSelector, - } = dispatch( ONBOARDING_STORE_NAME ); - invalidateResolution( 'getProfileItems', [] ); - invalidateResolution( 'getTasksStatus', [] ); - return { - clearTaskStatusCache: () => { - invalidateResolutionForStoreSelector( 'getTasksStatus' ); - invalidatePluginStoreSelector( 'getPaypalOnboardingStatus' ); - }, - createNotice, - installAndActivatePlugins, - updateOptions, - }; - } ), - withSelect( ( select, props ) => { - const { createNotice, installAndActivatePlugins } = props; + const { methods, options } = useSelect( ( select ) => { const { getProfileItems } = select( ONBOARDING_STORE_NAME ); const { getOption } = select( OPTIONS_STORE_NAME ); const { @@ -419,7 +94,7 @@ export default compose( 'woocommerce_woo-mercado-pago-basic_settings', ]; - const options = optionNames.reduce( ( result, name ) => { + const retrievedOptions = optionNames.reduce( ( result, name ) => { result[ name ] = getOption( name ); return result; }, {} ); @@ -433,27 +108,174 @@ export default compose( ? getPaypalOnboardingStatus() : null; - const methods = getPaymentMethods( { - activePlugins, - countryCode, - createNotice, - installAndActivatePlugins, - isJetpackConnected: isJetpackConnected(), - onboardingStatus, - options, - profileItems, - paypalOnboardingStatus, - loadingPaypalStatus: - ! hasFinishedResolution( 'getPaypalOnboardingStatus' ) && - ! paypalOnboardingStatus, + return { + methods: getPaymentMethods( { + activePlugins, + countryCode, + createNotice, + installAndActivatePlugins, + isJetpackConnected: isJetpackConnected(), + onboardingStatus, + options: retrievedOptions, + profileItems, + paypalOnboardingStatus, + loadingPaypalStatus: + ! hasFinishedResolution( 'getPaypalOnboardingStatus' ) && + ! paypalOnboardingStatus, + } ), + options: retrievedOptions, + }; + } ); + + const [ enabledMethods, setEnabledMethods ] = useState( + methods.reduce( ( acc, method ) => { + acc[ method.key ] = method.isEnabled; + return acc; + }, {} ) + ); + + const recommendedMethod = useMemo( () => { + const method = methods.find( + ( m ) => + ( m.key === 'wcpay' && m.visible ) || + ( m.key === 'mercadopago' && m.visible ) + ); + if ( ! method ) { + return 'stripe'; + } + return method.key; + }, [ methods ] ); + + const markConfigured = async ( methodKey, queryParams = {} ) => { + const method = methods.find( ( option ) => option.key === methodKey ); + + if ( ! method ) { + throw `Method ${ methodKey } not found in available methods list`; + } + + setEnabledMethods( { + ...enabledMethods, + [ methodKey ]: true, } ); - return { - countryCode, - profileItems, - activePlugins, - options, - methods, + const clearTaskStatusCache = () => { + invalidateResolution( 'getProfileItems', [] ); + invalidateResolution( 'getTasksStatus', [] ); + invalidateResolutionForStoreSelector( 'getTasksStatus' ); + invalidatePluginStoreSelector( 'getPaypalOnboardingStatus' ); }; - } ) -)( Payments ); + + await setMethodEnabledOption( method.optionName, 'yes', { + clearTaskStatusCache, + updateOptions, + options, + } ); + + recordEvent( 'tasklist_payment_connect_method', { + payment_method: methodKey, + } ); + + getHistory().push( + getNewPath( { ...queryParams, task: 'payments' }, '/', {} ) + ); + }; + + const currentMethod = useMemo( () => { + if ( ! query.method ) { + return null; + } + + const method = methods.find( ( m ) => m.key === query.method ); + + if ( ! method ) { + throw `Current method ${ query.method } not found in available methods list`; + } + + return method; + }, [ query ] ); + + if ( currentMethod ) { + return ( + + ); + } + + return ( +
+ { methods.map( ( method ) => { + const { + before, + container, + content, + isConfigured, + key, + manageUrl, + title, + visible, + loading, + onClick, + } = method; + + if ( ! visible ) { + return null; + } + + const classes = classnames( + 'woocommerce-task-payment', + 'woocommerce-task-card', + ! isConfigured && 'woocommerce-task-payment-not-configured', + 'woocommerce-task-payment-' + key + ); + + const isRecommended = + key === recommendedMethod && ! isConfigured; + + return ( + + { isRecommended && key !== 'wcpay' && ( + + ) } + { before } + + + { title } + { isRecommended && key === 'wcpay' && ( + + ) } + +
+ { content } +
+
+ + + recordEvent( 'tasklist_payment_setup', { + options: methods.map( + ( option ) => option.key + ), + selected: key, + } ) + } + onSetupCallback={ onClick } + /> + +
+ ); + } ) } +
+ ); +}; + +export default Payments; diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/bacs.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/bacs.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/bacs.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/bacs.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/eway.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/eway.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/eway.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/eway.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/methods.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js similarity index 92% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/methods.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js index 20c6a6a77bb..2c9389c7dd9 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/methods.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js @@ -14,12 +14,12 @@ import { Link } from '@woocommerce/components'; * Internal dependencies */ import Bacs from './bacs'; -import BacsLogo from './images/bacs'; -import CodLogo from './images/cod'; -import WCPayLogo from './images/wcpay'; -import RazorpayLogo from './images/razorpay'; -import { MollieLogo } from './images/mollie'; -import { PayUIndiaLogo } from './images/payu-india'; +import BacsLogo from '../images/bacs'; +import CodLogo from '../images/cod'; +import WCPayLogo from '../images/wcpay'; +import RazorpayLogo from '../images/razorpay'; +import { MollieLogo } from '../images/mollie'; +import { PayUIndiaLogo } from '../images/payu-india'; import Stripe from './stripe'; import Square from './square'; import { @@ -35,10 +35,16 @@ import EWay from './eway'; import Razorpay from './razorpay'; import { Mollie } from './mollie'; import { PayUIndia } from './payu-india'; -import { GenericPaymentStep } from './generic-payment-step'; +import { GenericPaymentStep } from '../generic-payment-step'; const wcAdminAssetUrl = getSetting( 'wcAdminAssetUrl', '' ); +const getPaymentsSettingsUrl = ( methodKey ) => { + return getAdminLink( + 'admin.php?page=wc-settings&tab=checkout§ion=' + methodKey + ); +}; + export function getPaymentMethods( { activePlugins, countryCode, @@ -98,6 +104,7 @@ export function getPaymentMethods( { options.woocommerce_stripe_settings && options.woocommerce_stripe_settings.enabled === 'yes', optionName: 'woocommerce_stripe_settings', + manageUrl: getPaymentsSettingsUrl( 'stripe' ), }, { key: 'paystack', @@ -149,6 +156,7 @@ export function getPaymentMethods( { }, }; }, + manageUrl: getPaymentsSettingsUrl( 'paystack' ), }, { key: 'payfast', @@ -209,6 +217,7 @@ export function getPaymentMethods( { }, }; }, + manageUrl: getPaymentsSettingsUrl( 'stripe' ), }, { key: 'mercadopago', @@ -241,6 +250,7 @@ export function getPaymentMethods( { options[ 'woocommerce_woo-mercado-pago-basic_settings' ] .enabled === 'yes', optionName: 'woocommerce_woo-mercado-pago-basic_settings', + manageUrl: getPaymentsSettingsUrl( 'woo-mercado-pago-basic' ), }, { key: 'paypal', @@ -266,6 +276,7 @@ export function getPaymentMethods( { loading: activePlugins.includes( PAYPAL_PLUGIN ) ? loadingPaypalStatus : false, + manageUrl: getPaymentsSettingsUrl( 'ppcp-gateway' ), }, { key: 'klarna_checkout', @@ -290,6 +301,7 @@ export function getPaymentMethods( { options.woocommerce_kco_settings && options.woocommerce_kco_settings.enabled === 'yes', optionName: 'woocommerce_kco_settings', + manageUrl: getPaymentsSettingsUrl( 'kco' ), }, { key: 'klarna_payments', @@ -325,6 +337,7 @@ export function getPaymentMethods( { options.woocommerce_klarna_payments_settings && options.woocommerce_klarna_payments_settings.enabled === 'yes', optionName: 'woocommerce_klarna_payments_settings', + manageUrl: getPaymentsSettingsUrl( 'klarna_payments' ), }, { key: 'mollie', @@ -360,6 +373,7 @@ export function getPaymentMethods( { options.woocommerce_mollie_payments_settings && options.woocommerce_mollie_payments_settings.enabled === 'yes', optionName: 'woocommerce_mollie_payments_settings', + manageUrl: getPaymentsSettingsUrl( 'mollie_wc_gateway_creditcard' ), }, { key: 'square', @@ -403,6 +417,7 @@ export function getPaymentMethods( { 'yes', optionName: 'woocommerce_square_credit_card_settings', hasCbdIndustry, + manageUrl: getPaymentsSettingsUrl( 'square_credit_card' ), }, { key: 'eway', @@ -432,6 +447,7 @@ export function getPaymentMethods( { options.woocommerce_eway_settings && options.woocommerce_eway_settings.enabled === 'yes', optionName: 'woocommerce_eway_settings', + manageUrl: getPaymentsSettingsUrl( 'eway' ), }, { key: 'razorpay', @@ -456,6 +472,7 @@ export function getPaymentMethods( { options.woocommerce_razorpay_settings && options.woocommerce_razorpay_settings.enabled === 'yes', optionName: 'woocommerce_razorpay_settings', + manageUrl: getPaymentsSettingsUrl( 'razorpay' ), }, { key: 'payubiz', @@ -475,6 +492,7 @@ export function getPaymentMethods( { isConfigured: activePlugins.includes( 'payu-india' ), isEnabled: enabledPaymentGateways.includes( 'payubiz' ), optionName: 'woocommerce_payubiz_settings', + manageUrl: getPaymentsSettingsUrl( 'payubiz' ), }, { key: 'cod', @@ -489,6 +507,7 @@ export function getPaymentMethods( { options.woocommerce_cod_settings && options.woocommerce_cod_settings.enabled === 'yes', optionName: 'woocommerce_cod_settings', + manageUrl: getPaymentsSettingsUrl( 'cod' ), }, { key: 'bacs', @@ -507,6 +526,7 @@ export function getPaymentMethods( { options.woocommerce_bacs_settings && options.woocommerce_bacs_settings.enabled === 'yes', optionName: 'woocommerce_bacs_settings', + manageUrl: getPaymentsSettingsUrl( 'bacs' ), }, ]; @@ -608,6 +628,7 @@ export function getPaymentMethods( { options.woocommerce_woocommerce_payments_settings.enabled === 'yes', optionName: 'woocommerce_woocommerce_payments_settings', + manageUrl: getPaymentsSettingsUrl( 'woocommerce_payments' ), } ); } diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/klarna.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/klarna.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/klarna.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/klarna.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/mercadopago.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/mercadopago.js similarity index 97% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/mercadopago.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/mercadopago.js index f190e16b96a..0771ba858f5 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/mercadopago.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/mercadopago.js @@ -12,7 +12,7 @@ import { SETTINGS_STORE_NAME } from '@woocommerce/data'; /** * Internal dependencies */ -import { getCountryCode } from '../../../dashboard/utils'; +import { getCountryCode } from '~/dashboard/utils'; export const MERCADOPAGO_PLUGIN = 'woocommerce-mercadopago'; diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/mollie.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/mollie.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/mollie.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/mollie.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/paypal.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/paypal.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/paypal.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/paypal.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/payu-india.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/payu-india.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/payu-india.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/payu-india.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/razorpay.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/razorpay.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/razorpay.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/razorpay.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/square.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/square.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/square.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/square.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/stripe.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/stripe.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/index.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/index.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/index.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/index.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/install-activate-and-connect.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/install-activate-and-connect.js similarity index 93% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/install-activate-and-connect.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/install-activate-and-connect.js index db9bfb94bc0..74683e2942b 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/install-activate-and-connect.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/install-activate-and-connect.js @@ -9,7 +9,7 @@ import { recordEvent } from '@woocommerce/tracks'; /** * Internal dependencies */ -import { createNoticesFromResponse } from '../../../../lib/notices'; +import { createNoticesFromResponse } from '~/lib/notices'; export function installActivateAndConnectWcpay( reject, diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/is-supported.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/is-supported.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/is-supported.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/is-supported.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/wcpay-usage-modal.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/wcpay-usage-modal.js similarity index 95% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/wcpay-usage-modal.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/wcpay-usage-modal.js index f62153c8c5e..efc0f213a59 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/wcpay-usage-modal.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/wcpay-usage-modal.js @@ -10,7 +10,7 @@ import { Link } from '@woocommerce/components'; /** * Internal dependencies */ -import UsageModal from '../../../../profile-wizard/steps/usage-modal'; +import UsageModal from '~/profile-wizard/steps/usage-modal'; const WCPayUsageModal = () => { const query = getQuery(); diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/wcpay.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/wcpay.js similarity index 100% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/wcpay/wcpay.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/methods/wcpay/wcpay.js diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/recommended-ribbon.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/recommended-ribbon.js new file mode 100644 index 00000000000..73d4019d24f --- /dev/null +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/recommended-ribbon.js @@ -0,0 +1,22 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +const localPartners = [ 'mercadopago' ]; + +export const RecommendedRibbon = ( { methodKey, isPill = false } ) => { + const classes = isPill + ? 'woocommerce-task-payment__recommended-pill' + : 'woocommerce-task-payment__recommended-ribbon'; + + const text = localPartners.includes( methodKey ) + ? __( 'Local Partner', 'woocommerce-admin' ) + : __( 'Recommended', 'woocommerce-admin' ); + + return ( +
+ { text } +
+ ); +}; diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/setup.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/setup.js new file mode 100644 index 00000000000..038ec1034bc --- /dev/null +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/setup.js @@ -0,0 +1,81 @@ +/** + * External dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { Card, CardBody } from '@wordpress/components'; +import { cloneElement, useMemo } from '@wordpress/element'; +import { Plugins } from '@woocommerce/components'; +import { PLUGINS_STORE_NAME, pluginNames } from '@woocommerce/data'; +import { recordEvent } from '@woocommerce/tracks'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { createNoticesFromResponse } from '~/lib/notices'; + +export const Setup = ( { method, markConfigured, query } ) => { + const { activePlugins } = useSelect( ( select ) => { + const { getActivePlugins } = select( PLUGINS_STORE_NAME ); + + return { + activePlugins: getActivePlugins(), + }; + } ); + + const installStep = useMemo( () => { + if ( ! method.plugins || ! method.plugins.length ) { + return; + } + + const pluginsToInstall = method.plugins.filter( + ( m ) => ! activePlugins.includes( m ) + ); + const pluginNamesString = method.plugins + .map( ( pluginSlug ) => pluginNames[ pluginSlug ] ) + .join( ' ' + __( 'and', 'woocommerce-admin' ) + ' ' ); + + return { + key: 'install', + label: sprintf( + /* translators: %s = one or more plugin names joined by "and" */ + __( 'Install %s', 'woocommerce-admin' ), + pluginNamesString + ), + content: ( + { + createNoticesFromResponse( response ); + recordEvent( 'tasklist_payment_install_method', { + plugins: method.plugins, + } ); + } } + onError={ ( errors, response ) => + createNoticesFromResponse( response ) + } + autoInstall + pluginSlugs={ method.plugins } + /> + ), + isComplete: ! pluginsToInstall.length, + }; + }, [ activePlugins, method.plugins ] ); + + if ( ! method.container ) { + return null; + } + + return ( + + + { cloneElement( method.container, { + methodConfig: method, + query, + installStep, + markConfigured, + hasCbdIndustry: method.hasCbdIndustry, + } ) } + + + ); +}; diff --git a/plugins/woocommerce-admin/client/task-list/test/payments.js b/plugins/woocommerce-admin/client/task-list/test/payments.js index f2bfebde652..9fdb7103842 100644 --- a/plugins/woocommerce-admin/client/task-list/test/payments.js +++ b/plugins/woocommerce-admin/client/task-list/test/payments.js @@ -8,7 +8,7 @@ import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies */ -import { PayPal, PAYPAL_PLUGIN } from '../tasks/payments/paypal'; +import { PayPal, PAYPAL_PLUGIN } from '../tasks/payments/methods/paypal'; import { getPaymentMethods } from '../tasks/payments/methods'; import { setMethodEnabledOption } from '../../task-list/tasks/payments'; import { GenericPaymentStep } from '../tasks/payments/generic-payment-step'; diff --git a/plugins/woocommerce-admin/readme.txt b/plugins/woocommerce-admin/readme.txt index acc76cfa254..a57adc730f6 100644 --- a/plugins/woocommerce-admin/readme.txt +++ b/plugins/woocommerce-admin/readme.txt @@ -80,6 +80,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt - Fix: Retain persisted queries when navigating to Homescreen #6614 - Update: Update choose niche note cta URL #6733 - Fix: Update folded header style #6724 +- Tweak: Refactor payments to allow management of methods #6786 - Fix: Fix unreleated variations showing up in the Products reports #6647 - Tweak: Add tracking data for the preview site btn #6623 - Tweak: Update WC Payments copy on the task list #6734 diff --git a/plugins/woocommerce-admin/tests/e2e/pages/PaymentsSetup.ts b/plugins/woocommerce-admin/tests/e2e/pages/PaymentsSetup.ts index b692fea1086..35c3795ddd7 100644 --- a/plugins/woocommerce-admin/tests/e2e/pages/PaymentsSetup.ts +++ b/plugins/woocommerce-admin/tests/e2e/pages/PaymentsSetup.ts @@ -3,6 +3,7 @@ */ import { waitForElementByText } from '../utils/actions'; import { BasePage } from './BasePage'; +import { getElementByText } from '../utils/actions'; type PaymentMethodWithSetupButton = | 'wcpay' @@ -39,15 +40,10 @@ export class PaymentsSetup extends BasePage { } async methodHasBeenSetup( method: PaymentMethod ) { - const toggle = this.getFormToggle( - `.woocommerce-task-payment-${ method }` - ); - - await toggle.isEnabled(); + await getElementByText( 'button', 'Manage', `.woocommerce-task-payment-${ method }` ) } async enableCashOnDelivery() { - const toggle = this.getFormToggle( '.woocommerce-task-payment-cod' ); - await toggle.switchOn(); + await this.clickButtonWithText( 'Enable' ); } } diff --git a/plugins/woocommerce-admin/tests/e2e/specs/tasks/payment.test.ts b/plugins/woocommerce-admin/tests/e2e/specs/tasks/payment.test.ts index 59fc6aba8e7..7ff7851f00c 100644 --- a/plugins/woocommerce-admin/tests/e2e/specs/tasks/payment.test.ts +++ b/plugins/woocommerce-admin/tests/e2e/specs/tasks/payment.test.ts @@ -53,7 +53,7 @@ describe( 'Payment setup task', () => { await paymentsSetup.methodHasBeenSetup( 'bacs' ); } ); - it( 'Toggling cash on delivery enables the payment method', async () => { + it( 'Enabling cash on delivery enables the payment method', async () => { await paymentsSetup.enableCashOnDelivery(); await paymentsSetup.methodHasBeenSetup( 'cod' ); } );