diff --git a/packages/js/data/changelog/add-react-main-payments-settings-screen b/packages/js/data/changelog/add-react-main-payments-settings-screen new file mode 100644 index 00000000000..0aa0b00a417 --- /dev/null +++ b/packages/js/data/changelog/add-react-main-payments-settings-screen @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix payment store selector type diff --git a/packages/js/data/src/payment-gateways/index.ts b/packages/js/data/src/payment-gateways/index.ts index b9c74e18f19..afd3e78c926 100644 --- a/packages/js/data/src/payment-gateways/index.ts +++ b/packages/js/data/src/payment-gateways/index.ts @@ -13,7 +13,7 @@ import * as resolvers from './resolvers'; import * as selectors from './selectors'; import reducer from './reducer'; import { STORE_KEY } from './constants'; -import { WPDataActions } from '../types'; +import { WPDataSelectors } from '../types'; import { PromiseifySelectors } from '../types/promiseify-selectors'; export const PAYMENT_GATEWAYS_STORE_NAME = STORE_KEY; @@ -33,7 +33,7 @@ declare module '@wordpress/data' { ): DispatchFromMap< typeof actions >; function select( key: typeof STORE_KEY - ): SelectFromMap< typeof selectors > & WPDataActions; + ): SelectFromMap< typeof selectors > & WPDataSelectors; function resolveSelect( key: typeof STORE_KEY ): PromiseifySelectors< SelectFromMap< typeof selectors > >; diff --git a/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx b/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx new file mode 100644 index 00000000000..49228cb4276 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx @@ -0,0 +1,155 @@ +/** + * External dependencies + */ +import React, { useMemo } from '@wordpress/element'; +import { Button } from '@wordpress/components'; +import ExternalIcon from 'gridicons/dist/external'; +import { __, _x } from '@wordpress/i18n'; +import { + ONBOARDING_STORE_NAME, + PAYMENT_GATEWAYS_STORE_NAME, + SETTINGS_STORE_NAME, +} from '@woocommerce/data'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { getCountryCode } from '~/dashboard/utils'; +import { + getEnrichedPaymentGateways, + getIsGatewayWCPay, + getIsWCPayOrOtherCategoryDoneSetup, + getSplitGateways, +} from '~/task-lists/fills/PaymentGatewaySuggestions/utils'; + +type PaymentGateway = { + id: string; + image_72x72: string; + title: string; + enabled: boolean; + needsSetup: boolean; + // Add other properties as needed... +}; + +const usePaymentGatewayData = () => { + return useSelect( ( select ) => { + const { getSettings } = select( SETTINGS_STORE_NAME ); + const { general: settings = {} } = getSettings( 'general' ); + return { + getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME ) + .getPaymentGateway, + installedPaymentGateways: select( + PAYMENT_GATEWAYS_STORE_NAME + ).getPaymentGateways(), + isResolving: select( ONBOARDING_STORE_NAME ).isResolving( + 'getPaymentGatewaySuggestions' + ), + paymentGatewaySuggestions: select( + ONBOARDING_STORE_NAME + ).getPaymentGatewaySuggestions(), + countryCode: getCountryCode( settings.woocommerce_default_country ), + }; + }, [] ); +}; + +const AdditionalGatewayImages = ( { + additionalGateways, +}: { + additionalGateways: PaymentGateway[]; +} ) => ( + <> + { additionalGateways.map( ( gateway ) => ( + { + ) ) } + { _x( '& more.', 'More payment providers to discover', 'woocommerce' ) } + +); + +export const OtherPaymentMethods = () => { + const { + paymentGatewaySuggestions, + installedPaymentGateways, + isResolving, + countryCode, + } = usePaymentGatewayData(); + + const paymentGateways = useMemo( + () => + getEnrichedPaymentGateways( + installedPaymentGateways, + paymentGatewaySuggestions + ), + [ installedPaymentGateways, paymentGatewaySuggestions ] + ); + + const isWCPayOrOtherCategoryDoneSetup = useMemo( + () => + getIsWCPayOrOtherCategoryDoneSetup( paymentGateways, countryCode ), + [ countryCode, paymentGateways ] + ); + + const isWCPaySupported = Array.from( paymentGateways.values() ).some( + getIsGatewayWCPay + ); + + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars + const [ wcPayGateway, _offlineGateways, additionalGateways ] = useMemo( + () => + getSplitGateways( + paymentGateways, + countryCode ?? '', + isWCPaySupported, + isWCPayOrOtherCategoryDoneSetup + ), + [ + paymentGateways, + countryCode, + isWCPaySupported, + isWCPayOrOtherCategoryDoneSetup, + ] + ); + + if ( isResolving || ! wcPayGateway ) { + return null; + } + + const hasWcPaySetup = wcPayGateway.enabled && ! wcPayGateway.needsSetup; + + return ( + <> + + { additionalGateways.length > 0 && ( + + ) } + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx b/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx new file mode 100644 index 00000000000..fa7adf07c28 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx @@ -0,0 +1,183 @@ +/** + * External dependencies + */ +import { useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { PaymentGateway } from '@woocommerce/data'; +import { WooPaymentMethodsLogos } from '@woocommerce/onboarding'; + +/** + * Internal dependencies + */ +import { getAdminSetting } from '~/utils/admin-settings'; +import sanitizeHTML from '~/lib/sanitize-html'; +import { WCPayInstallButton } from './wcpay-install-button'; + +export const PaymentMethod = ( { + id, + enabled, + title, + method_title, + method_description, + settings_url, +}: PaymentGateway ) => { + const isWooPayEligible = getAdminSetting( 'isWooPayEligible', false ); + const [ isEnabled, setIsEnabled ] = useState( enabled ); + const [ isLoading, setIsLoading ] = useState( false ); + + const toggleEnabled = async ( e: React.MouseEvent ) => { + e.preventDefault(); + setIsLoading( true ); + + if ( ! window.woocommerce_admin.nonces?.gateway_toggle ) { + // eslint-disable-next-line no-console + console.warn( 'Unexpected error: Nonce not found' ); + // Redirect to payment setting page if nonce is not found. Users should still be able to toggle the payment method from that page. + window.location.href = settings_url; + return; + } + + try { + const response = await fetch( window.woocommerce_admin.ajax_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'woocommerce_toggle_gateway_enabled', + security: window.woocommerce_admin.nonces?.gateway_toggle, + gateway_id: id, + } ), + } ); + + const result = await response.json(); + + if ( result.success ) { + if ( result.data === true ) { + setIsEnabled( true ); + } else if ( result.data === false ) { + setIsEnabled( false ); + } else if ( result.data === 'needs_setup' ) { + window.location.href = settings_url; + } + } else { + window.location.href = settings_url; + } + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( 'Error toggling gateway:', error ); + } finally { + setIsLoading( false ); + } + }; + + return ( + + + +
+ + { method_title } + + { id !== 'pre_install_woocommerce_payments_promotion' && + method_title !== title && ( + +  –  + { title } + + ) } + { id === 'pre_install_woocommerce_payments_promotion' && ( +
+ +
+ ) } +
+ + + + + { isEnabled + ? __( 'Yes', 'woocommerce' ) + : __( 'No', 'woocommerce' ) } + + + + + + { id === 'pre_install_woocommerce_payments_promotion' ? ( + + ) : ( + + { enabled + ? __( 'Manage', 'woocommerce' ) + : __( 'Finish setup', 'woocommerce' ) } + + ) } + + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx b/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx new file mode 100644 index 00000000000..a5583bd73f6 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx @@ -0,0 +1,60 @@ +/** + * External dependencies + */ +import React, { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + PAYMENT_GATEWAYS_STORE_NAME, + PLUGINS_STORE_NAME, +} from '@woocommerce/data'; +import { Button } from '@wordpress/components'; +import { resolveSelect, useDispatch } from '@wordpress/data'; +import { recordEvent } from '@woocommerce/tracks'; + +const slug = 'woocommerce-payments'; +export const WCPayInstallButton = () => { + const [ installing, setInstalling ] = useState( false ); + const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME ); + const { createNotice } = useDispatch( 'core/notices' ); + + const redirectToSettings = async () => { + const paymentGateway = await resolveSelect( + PAYMENT_GATEWAYS_STORE_NAME + ).getPaymentGateway( slug.replace( /-/g, '_' ) ); + + if ( paymentGateway?.settings_url ) { + window.location.href = paymentGateway.settings_url; + } + }; + + const installWooCommercePayments = async () => { + if ( installing ) return; + + setInstalling( true ); + recordEvent( 'settings_payments_recommendations_setup', { + extension_selected: slug, + } ); + + try { + await installAndActivatePlugins( [ slug ] ); + redirectToSettings(); + } catch ( error ) { + if ( error instanceof Error ) { + createNotice( 'error', error.message ); + } + setInstalling( false ); + } + }; + + return ( + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss index a63d140104c..64beb4baac8 100644 --- a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss +++ b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss @@ -1,9 +1,29 @@ +@import "~/wp-admin-scripts/payment-method-promotions/payment-promotion-row.scss"; + .settings-payments-main__container { - h1 { - color: #fff; + .settings-payments-main__spinner { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + width: 100%; + } + + + table.wc_gateways { + .other-payment-methods__button-text { + margin-right: 4px; + } + + td.other-payment-methods-row { + border-top: 1px solid #c3c4c7; + background-color: #fff; + + } + + .other-payment-methods__image { + vertical-align: middle; + margin-right: 8px; + } } - background: #000; - text-align: center; - padding: 50px 0; - width: 100%; } diff --git a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx index c2671e205bf..94f85355931 100644 --- a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx +++ b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx @@ -1,17 +1,108 @@ /** * External dependencies */ -import '@wordpress/element'; +import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { PaymentGateway } from '@woocommerce/data'; /** * Internal dependencies */ import './settings-payments-main.scss'; +import { PaymentMethod } from './components/payment-method'; +import { OtherPaymentMethods } from './components/other-payment-methods'; +import { PaymentsBannerWrapper } from '~/payments/payment-settings-banner'; export const SettingsPaymentsMain: React.FC = () => { + const [ paymentGateways, error ] = useMemo( () => { + const script = document.getElementById( + 'experimental_wc_settings_payments_gateways' + ); + + try { + if ( script && script.textContent ) { + return [ + JSON.parse( script.textContent ) as PaymentGateway[], + null, + ]; + } + throw new Error( 'Could not find payment gateways data' ); + } catch ( e ) { + return [ [], e as Error ]; + } + }, [] ); + + if ( error ) { + // This is a temporary error message to be replaced by error boundary. + return ( +
+

+ { __( 'Error loading payment gateways', 'woocommerce' ) } +

+

{ error.message }

+
+ ); + } + return (
-

Main payments screen

+
+ +
+ + + + + + +
+ + + + + + + + + + + + { paymentGateways.map( + ( gateway: PaymentGateway ) => ( + + ) + ) } + + + + + +
+ { __( 'Method', 'woocommerce' ) } + + { __( 'Enabled', 'woocommerce' ) } + + { __( + 'Description', + 'woocommerce' + ) } +
+ +
+
); }; diff --git a/plugins/woocommerce-admin/client/typings/global.d.ts b/plugins/woocommerce-admin/client/typings/global.d.ts index 7e0aeebf77c..9c3a8c79e4b 100644 --- a/plugins/woocommerce-admin/client/typings/global.d.ts +++ b/plugins/woocommerce-admin/client/typings/global.d.ts @@ -88,6 +88,12 @@ declare global { getUserSetting?: ( name: string ) => string | undefined; setUserSetting?: ( name: string, value: string ) => void; deleteUserSetting?: ( name: string ) => void; + woocommerce_admin: { + ajax_url: string; + nonces: { + gateway_toggle?: string; + } + } } } diff --git a/plugins/woocommerce/changelog/add-react-main-payments-settings-screen b/plugins/woocommerce/changelog/add-react-main-payments-settings-screen new file mode 100644 index 00000000000..d92bea60fbb --- /dev/null +++ b/plugins/woocommerce/changelog/add-react-main-payments-settings-screen @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add react-powered main payments settings screen diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php index 178c33a4daf..88ee03acd20 100644 --- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php +++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php @@ -57,6 +57,16 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page { //phpcs:disable WordPress.Security.NonceVerification.Recommended global $current_section; + // We don't want to output anything from the action for now. So we buffer it and discard it. + ob_start(); + /** + * Fires before the payment gateways settings fields are rendered. + * + * @since 1.5.7 + */ + do_action( 'woocommerce_admin_field_payment_gateways' ); + ob_end_clean(); + // Load gateways so we can show any global options they may have. $payment_gateways = WC()->payment_gateways->payment_gateways(); @@ -91,6 +101,11 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page { global $hide_save_button; $hide_save_button = true; echo '
'; + + // Output the gateways data to the page so the React app can use it. + $controller = new WC_REST_Payment_Gateways_Controller(); + $response = $controller->get_items( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways' ) ); + echo ''; } /**