From a06a74004c7e065e1c53d8174457269acf1fc0c0 Mon Sep 17 00:00:00 2001 From: Himad M Date: Mon, 2 Dec 2024 18:15:07 -0400 Subject: [PATCH 01/93] New Settings UI: Map storeCountry and currency to WooCommerce settings --- .../AcdcOptionalPaymentMethods.js | 4 +-- .../WelcomeDocs/AcdcFlow.js | 4 +-- .../WelcomeDocs/BcdcFlow.js | 2 +- .../Screens/Onboarding/StepPaymentMethods.js | 9 ++++-- .../Screens/Onboarding/StepWelcome.js | 6 ++-- .../resources/js/data/common/hooks.js | 8 ++++++ .../resources/js/data/common/reducer.js | 9 +++++- .../resources/js/utils/countryPriceInfo.js | 16 +++++------ modules/ppcp-settings/services.php | 5 +++- .../ppcp-settings/src/Data/CommonSettings.php | 28 +++++++++++++++++++ .../src/Endpoint/CommonRestEndpoint.php | 28 +++++++++++++++++-- 11 files changed, 97 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js index f316a9a90..733d410de 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js @@ -1,4 +1,4 @@ -import BadgeBox, { BADGE_BOX_TITLE_BIG } from '../BadgeBox'; +import BadgeBox from '../BadgeBox'; import { __, sprintf } from '@wordpress/i18n'; import Separator from '../Separator'; import generatePriceText from '../../../utils/badgeBoxUtils'; @@ -10,7 +10,7 @@ const AcdcOptionalPaymentMethods = ( { storeCountry, storeCurrency, } ) => { - if ( isFastlane && isPayLater && storeCountry === 'us' ) { + if ( isFastlane && isPayLater && storeCountry === 'US' ) { return (
{ - if ( isFastlane && isPayLater && storeCountry === 'us' ) { + if ( isFastlane && isPayLater && storeCountry === 'US' ) { return (
@@ -123,7 +123,7 @@ const AcdcFlow = ( { ); } - if ( isPayLater && storeCountry === 'uk' ) { + if ( isPayLater && storeCountry === 'UK' ) { return (
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js index 6c984cfc1..325e40a5e 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js @@ -6,7 +6,7 @@ import { countryPriceInfo } from '../../../utils/countryPriceInfo'; import OptionalPaymentMethods from '../OptionalPaymentMethods/OptionalPaymentMethods'; const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { - if ( isPayLater && storeCountry === 'us' ) { + if ( isPayLater && storeCountry === 'US' ) { return (
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js index a9d2f6b9e..83ca5540f 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js @@ -3,7 +3,7 @@ import { __, sprintf } from '@wordpress/i18n'; import OnboardingHeader from '../../ReusableComponents/OnboardingHeader'; import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; import SelectBox from '../../ReusableComponents/SelectBox'; -import { OnboardingHooks } from '../../../data'; +import { CommonHooks, OnboardingHooks } from '../../../data'; import OptionalPaymentMethods from '../../ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods'; const OPM_RADIO_GROUP_NAME = 'optional-payment-methods'; @@ -13,6 +13,9 @@ const StepPaymentMethods = ( {} ) => { areOptionalPaymentMethodsEnabled, setAreOptionalPaymentMethodsEnabled, } = OnboardingHooks.useOptionalPaymentMethods(); + + const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + const pricesBasedDescription = sprintf( // translators: %s: Link to PayPal REST application guide __( @@ -42,8 +45,8 @@ const StepPaymentMethods = ( {} ) => { useAcdc={ true } isFastlane={ true } isPayLater={ true } - storeCountry={ 'us' } - storeCurrency={ 'usd' } + storeCountry={ storeCountry } + storeCurrency={ storeCurrency } /> } name={ OPM_RADIO_GROUP_NAME } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js index c94c84935..761093b24 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -8,8 +8,10 @@ import WelcomeDocs from '../../ReusableComponents/WelcomeDocs/WelcomeDocs'; import AccordionSection from '../../ReusableComponents/AccordionSection'; import AdvancedOptionsForm from './Components/AdvancedOptionsForm'; +import { CommonHooks } from '../../../data'; const StepWelcome = ( { setStep, currentStep, setCompleted } ) => { + const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); return (
{ useAcdc={ true } isFastlane={ true } isPayLater={ true } - storeCountry={ 'us' } - storeCurrency={ 'USD' } + storeCountry={ storeCountry } + storeCurrency={ storeCurrency } /> { const clientSecret = usePersistent( 'clientSecret' ); const isSandboxMode = usePersistent( 'useSandbox' ); const isManualConnectionMode = usePersistent( 'useManualConnection' ); + const flags = usePersistent( 'flags' ); const savePersistent = async ( setter, value ) => { setter( value ); @@ -69,6 +70,7 @@ const useHooks = () => { }, connectViaSandbox, connectViaIdAndSecret, + flags, }; }; @@ -109,3 +111,9 @@ export const useManualConnection = () => { connectViaIdAndSecret, }; }; + +export const useWooSettings = () => { + const { flags = {} } = useHooks(); + const { country, currency } = flags; + return { storeCountry: country, storeCurrency: currency }; +}; diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 3f822468b..cdf7841f5 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -22,6 +22,10 @@ const defaultPersistent = { useManualConnection: false, clientId: '', clientSecret: '', + flags: { + country: '', + currency: '', + }, }; // Reducer logic. @@ -39,7 +43,10 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { setPersistent( state, action ), [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => - setPersistent( state, payload.data ), + setPersistent( state, { + ...payload.data, + flags: payload.flags, + } ), } ); export default commonReducer; diff --git a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js index 193efd584..34bfc8e7f 100644 --- a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js +++ b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js @@ -1,5 +1,5 @@ export const countryPriceInfo = { - us: { + US: { currencySymbol: '$', fixedFee: 0.49, checkout: 3.49, @@ -9,7 +9,7 @@ export const countryPriceInfo = { fastlane: 2.59, standardCardFields: 2.99, }, - uk: { + UK: { currencySymbol: '£', fixedFee: 0.3, checkout: 2.9, @@ -18,7 +18,7 @@ export const countryPriceInfo = { apm: 1.2, standardCardFields: 1.2, }, - ca: { + CA: { currencySymbol: '$', fixedFee: 0.3, checkout: 2.9, @@ -27,7 +27,7 @@ export const countryPriceInfo = { apm: 2.9, standardCardFields: 2.9, }, - au: { + AU: { currencySymbol: '$', fixedFee: 0.3, checkout: 2.6, @@ -36,7 +36,7 @@ export const countryPriceInfo = { apm: 2.6, standardCardFields: 2.6, }, - fr: { + FR: { currencySymbol: '€', fixedFee: 0.35, checkout: 2.9, @@ -45,7 +45,7 @@ export const countryPriceInfo = { apm: 1.2, standardCardFields: 1.2, }, - it: { + IT: { currencySymbol: '€', fixedFee: 0.35, checkout: 3.4, @@ -54,7 +54,7 @@ export const countryPriceInfo = { apm: 1.2, standardCardFields: 1.2, }, - de: { + DE: { currencySymbol: '€', fixedFee: 0.39, checkout: 2.99, @@ -63,7 +63,7 @@ export const countryPriceInfo = { apm: 2.99, standardCardFields: 2.99, }, - es: { + ES: { currencySymbol: '€', fixedFee: 0.35, checkout: 2.9, diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index d213aa4c0..2c646f054 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -54,7 +54,10 @@ return new GeneralSettings(); }, 'settings.data.common' => static function ( ContainerInterface $container ) : CommonSettings { - return new CommonSettings(); + return new CommonSettings( + $container->get( 'api.shop.country' ), + $container->get( 'api.shop.currency.getter' )->get(), + ); }, 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); diff --git a/modules/ppcp-settings/src/Data/CommonSettings.php b/modules/ppcp-settings/src/Data/CommonSettings.php index 8f7dd1ddf..f480e7c81 100644 --- a/modules/ppcp-settings/src/Data/CommonSettings.php +++ b/modules/ppcp-settings/src/Data/CommonSettings.php @@ -29,6 +29,25 @@ class CommonSettings extends AbstractDataModel { */ protected const OPTION_KEY = 'woocommerce-ppcp-data-common'; + /** + * List of customization flags, provided by the server (read-only). + * + * @var array + */ + protected array $flags = array(); + + /** + * Constructor. + * + * @param string $country WooCommerce store country. + * @param string $currency WooCommerce store currency. + */ + public function __construct( string $country, string $currency ) { + parent::__construct(); + $this->flags['country'] = $country; + $this->flags['currency'] = $currency; + } + /** * Get default values for the model. * @@ -116,4 +135,13 @@ public function get_client_secret() : string { public function set_client_secret( string $client_secret ) : void { $this->data['client_secret'] = sanitize_text_field( $client_secret ); } + + /** + * Returns the list of read-only customization flags + * + * @return array + */ + public function get_flags() : array { + return $this->flags; + } } diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index c7345148e..0c197ef67 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -60,6 +60,20 @@ class CommonRestEndpoint extends RestEndpoint { ), ); + /** + * Map the internal flags to JS names. + * + * @var array + */ + private array $flag_map = array( + 'country' => array( + 'js_name' => 'country', + ), + 'currency' => array( + 'js_name' => 'currency', + ), + ); + /** * Constructor. * @@ -103,13 +117,23 @@ public function register_routes() { * * @return WP_REST_Response The common settings. */ - public function get_details() : WP_REST_Response { + public function get_details(): WP_REST_Response { $js_data = $this->sanitize_for_javascript( $this->settings->to_array(), $this->field_map ); - return $this->return_success( $js_data ); + $js_flags = $this->sanitize_for_javascript( + $this->settings->get_flags(), + $this->flag_map + ); + + return $this->return_success( + $js_data, + array( + 'flags' => $js_flags, + ) + ); } /** From 5a014e24b21b7a18181b89634857df4b22028c6b Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 3 Dec 2024 15:27:16 +0400 Subject: [PATCH 02/93] Add more APMs and their descriptions from the old settings --- .../icon-button-payment-method-multibanco.svg | 1 + .../icon-button-payment-method-mybank.svg | 28 +++++++++++++ .../icon-button-payment-method-przelewy24.svg | 38 ++++++++++++++++++ .../icon-button-payment-method-trustly.svg | 1 + .../Screens/Overview/TabPaymentMethods.js | 40 ++++++++++++++++++- 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-multibanco.svg create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-mybank.svg create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-przelewy24.svg create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-trustly.svg diff --git a/modules/ppcp-settings/images/icon-button-payment-method-multibanco.svg b/modules/ppcp-settings/images/icon-button-payment-method-multibanco.svg new file mode 100644 index 000000000..9d423223c --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-multibanco.svg @@ -0,0 +1 @@ +Logo_Multibanco diff --git a/modules/ppcp-settings/images/icon-button-payment-method-mybank.svg b/modules/ppcp-settings/images/icon-button-payment-method-mybank.svg new file mode 100644 index 000000000..82dd40ca4 --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-mybank.svg @@ -0,0 +1,28 @@ + + + + MyBank + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ppcp-settings/images/icon-button-payment-method-przelewy24.svg b/modules/ppcp-settings/images/icon-button-payment-method-przelewy24.svg new file mode 100644 index 000000000..3ab7a31be --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-przelewy24.svg @@ -0,0 +1,38 @@ + + + + logo P24 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ppcp-settings/images/icon-button-payment-method-trustly.svg b/modules/ppcp-settings/images/icon-button-payment-method-trustly.svg new file mode 100644 index 000000000..85bfacbe0 --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-trustly.svg @@ -0,0 +1 @@ + diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js index 453a34426..8e81aac8e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js @@ -173,7 +173,7 @@ const paymentMethodsAlternativeDefault = [ id: 'eps', title: __( 'eps', 'woocommerce-paypal-payments' ), description: __( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor massa ex, eget luctus lacus iaculis at.', + 'An online payment method in Austria, enabling Austrian buyers to make secure payments directly through their bank accounts. Transactions are processed in EUR.', 'woocommerce-paypal-payments' ), icon: 'payment-method-eps', @@ -182,11 +182,47 @@ const paymentMethodsAlternativeDefault = [ id: 'blik', title: __( 'BLIK', 'woocommerce-paypal-payments' ), description: __( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum porttitor massa ex, eget luctus lacus iaculis at.', + 'A widely used mobile payment method in Poland, allowing Polish customers to pay directly via their banking apps. Transactions are processed in PLN.', 'woocommerce-paypal-payments' ), icon: 'payment-method-blik', }, + { + id: 'mybank', + title: __( 'MyBank', 'woocommerce-paypal-payments' ), + description: __( + 'A European online banking payment solution primarily used in Italy, enabling customers to make secure bank transfers during checkout. Transactions are processed in EUR.', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-mybank', + }, + { + id: 'przelewy24', + title: __( 'Przelewy24', 'woocommerce-paypal-payments' ), + description: __( + 'A popular online payment gateway in Poland, offering various payment options for Polish customers. Transactions can be processed in PLN or EUR.', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-przelewy24', + }, + { + id: 'trustly', + title: __( 'Trustly', 'woocommerce-paypal-payments' ), + description: __( + 'A European payment method that allows buyers to make payments directly from their bank accounts, suitable for customers across multiple European countries. Supported currencies include EUR, DKK, SEK, GBP, and NOK.', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-trustly', + }, + { + id: 'multibanco', + title: __( 'Multibanco', 'woocommerce-paypal-payments' ), + description: __( + 'An online payment method in Portugal, enabling Portuguese buyers to make secure payments directly through their bank accounts. Transactions are processed in EUR.', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-multibanco', + }, ]; export default TabPaymentMethods; From fbbec10ea31987e3c91b3ba43f0fc48a66ae9574 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 3 Dec 2024 18:06:23 +0400 Subject: [PATCH 03/93] Conditionally add PUI and OXXO --- .../icon-button-payment-method-oxxo.svg | 18 ++++++++++ .../icon-button-payment-method-ratepay.svg | 3 ++ .../Screens/Overview/TabPaymentMethods.js | 35 ++++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-oxxo.svg create mode 100644 modules/ppcp-settings/images/icon-button-payment-method-ratepay.svg diff --git a/modules/ppcp-settings/images/icon-button-payment-method-oxxo.svg b/modules/ppcp-settings/images/icon-button-payment-method-oxxo.svg new file mode 100644 index 000000000..4f69e152d --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-oxxo.svg @@ -0,0 +1,18 @@ + + + + logo OXXO + Created with Sketch. + + + + + + + + + + + + + diff --git a/modules/ppcp-settings/images/icon-button-payment-method-ratepay.svg b/modules/ppcp-settings/images/icon-button-payment-method-ratepay.svg new file mode 100644 index 000000000..f0da1b689 --- /dev/null +++ b/modules/ppcp-settings/images/icon-button-payment-method-ratepay.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js index 8e81aac8e..c0dec5075 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js @@ -4,12 +4,25 @@ import PaymentMethodItem from '../../ReusableComponents/PaymentMethodItem'; import ModalPayPal from './Modals/ModalPayPal'; import ModalFastlane from './Modals/ModalFastlane'; import ModalAcdc from './Modals/ModalAcdc'; +import { CommonHooks } from '../../../data'; const TabPaymentMethods = () => { const renderPaymentMethods = ( data ) => { + const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + + const conditionallyUpdatedPaymentMethods = [ + ...data, + ...( storeCountry === 'DE' && storeCurrency === 'EUR' + ? [ puiPaymentMethod ] + : [] ), + ...( storeCountry === 'MX' && storeCurrency === 'MXN' + ? [ oxxoPaymentMethod ] + : [] ), + ]; + return (
- { data.map( ( paymentMethod ) => ( + { conditionallyUpdatedPaymentMethods.map( ( paymentMethod ) => ( Date: Tue, 3 Dec 2024 15:24:49 -0400 Subject: [PATCH 04/93] New Settings UI: Apply feedback --- .../resources/js/data/common/hooks.js | 13 +++++++----- .../resources/js/data/common/reducer.js | 21 +++++++++++-------- .../resources/js/data/common/selectors.js | 6 +++++- .../ppcp-settings/src/Data/CommonSettings.php | 10 ++++----- .../src/Endpoint/CommonRestEndpoint.php | 14 ++++++------- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index a000ad9c8..fbe6a4842 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -43,7 +43,11 @@ const useHooks = () => { const clientSecret = usePersistent( 'clientSecret' ); const isSandboxMode = usePersistent( 'useSandbox' ); const isManualConnectionMode = usePersistent( 'useManualConnection' ); - const flags = usePersistent( 'flags' ); + + const wooSettings = useSelect( + ( select ) => select( STORE_NAME ).wooSettings(), + [] + ); const savePersistent = async ( setter, value ) => { setter( value ); @@ -70,7 +74,7 @@ const useHooks = () => { }, connectViaSandbox, connectViaIdAndSecret, - flags, + wooSettings, }; }; @@ -113,7 +117,6 @@ export const useManualConnection = () => { }; export const useWooSettings = () => { - const { flags = {} } = useHooks(); - const { country, currency } = flags; - return { storeCountry: country, storeCurrency: currency }; + const { wooSettings } = useHooks(); + return wooSettings; }; diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index cdf7841f5..87c47127a 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -22,10 +22,6 @@ const defaultPersistent = { useManualConnection: false, clientId: '', clientSecret: '', - flags: { - country: '', - currency: '', - }, }; // Reducer logic. @@ -42,11 +38,18 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { [ ACTION_TYPES.SET_PERSISTENT ]: ( state, action ) => setPersistent( state, action ), - [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => - setPersistent( state, { - ...payload.data, - flags: payload.flags, - } ), + [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { + const newState = setPersistent( state, payload.data ); + + if ( payload.wooSettings ) { + newState.wooSettings = { + ...newState.wooSettings, + ...payload.wooSettings, + }; + } + + return newState; + }, } ); export default commonReducer; diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js index 14334fcf3..57620d0dc 100644 --- a/modules/ppcp-settings/resources/js/data/common/selectors.js +++ b/modules/ppcp-settings/resources/js/data/common/selectors.js @@ -16,6 +16,10 @@ export const persistentData = ( state ) => { }; export const transientData = ( state ) => { - const { data, ...transientState } = getState( state ); + const { data, wooSettings, ...transientState } = getState( state ); // ← extract the wooSettings, to ensure they are not part of the "transientState" collection. return transientState || EMPTY_OBJ; }; + +export const wooSettings = ( state ) => { + return getState( state ).wooSettings || EMPTY_OBJ; +}; diff --git a/modules/ppcp-settings/src/Data/CommonSettings.php b/modules/ppcp-settings/src/Data/CommonSettings.php index f480e7c81..f49e2895b 100644 --- a/modules/ppcp-settings/src/Data/CommonSettings.php +++ b/modules/ppcp-settings/src/Data/CommonSettings.php @@ -34,7 +34,7 @@ class CommonSettings extends AbstractDataModel { * * @var array */ - protected array $flags = array(); + protected array $woo_settings = array(); /** * Constructor. @@ -44,8 +44,8 @@ class CommonSettings extends AbstractDataModel { */ public function __construct( string $country, string $currency ) { parent::__construct(); - $this->flags['country'] = $country; - $this->flags['currency'] = $currency; + $this->woo_settings['country'] = $country; + $this->woo_settings['currency'] = $currency; } /** @@ -141,7 +141,7 @@ public function set_client_secret( string $client_secret ) : void { * * @return array */ - public function get_flags() : array { - return $this->flags; + public function get_woo_settings() : array { + return $this->woo_settings; } } diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 0c197ef67..bebf9980c 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -65,12 +65,12 @@ class CommonRestEndpoint extends RestEndpoint { * * @var array */ - private array $flag_map = array( + private array $woo_settings_map = array( 'country' => array( - 'js_name' => 'country', + 'js_name' => 'storeCountry', ), 'currency' => array( - 'js_name' => 'currency', + 'js_name' => 'storeCurrency', ), ); @@ -123,15 +123,15 @@ public function get_details(): WP_REST_Response { $this->field_map ); - $js_flags = $this->sanitize_for_javascript( - $this->settings->get_flags(), - $this->flag_map + $js_woo_settings = $this->sanitize_for_javascript( + $this->settings->get_woo_settings(), + $this->woo_settings_map ); return $this->return_success( $js_data, array( - 'flags' => $js_flags, + 'wooSettings' => $js_woo_settings, ) ); } From 681f420b12a4a8aecd150cbaabb4822a76d45c31 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 4 Dec 2024 12:11:21 +0100 Subject: [PATCH 05/93] =?UTF-8?q?=E2=9C=A8=20Add=20Redux=20defaults=20for?= =?UTF-8?q?=20read-only=20wooSettings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/resources/js/data/common/reducer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 87c47127a..771dfa8f5 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -15,6 +15,12 @@ import ACTION_TYPES from './action-types'; const defaultTransient = { isReady: false, isBusy: false, + + // Read only values, provided by the server via hydrate. + wooSettings: { + storeCountry: '', + storeCurrency: '', + }, }; const defaultPersistent = { From 4d6bad1ffd3008a5d54350ed09880d39537bf8e1 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 4 Dec 2024 12:18:19 +0100 Subject: [PATCH 06/93] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20code=20style=20(co?= =?UTF-8?q?mment=20&=20whitespace)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/resources/js/data/common/selectors.js | 2 +- modules/ppcp-settings/src/Data/CommonSettings.php | 4 ++-- modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js index 57620d0dc..7f0b3ee20 100644 --- a/modules/ppcp-settings/resources/js/data/common/selectors.js +++ b/modules/ppcp-settings/resources/js/data/common/selectors.js @@ -16,7 +16,7 @@ export const persistentData = ( state ) => { }; export const transientData = ( state ) => { - const { data, wooSettings, ...transientState } = getState( state ); // ← extract the wooSettings, to ensure they are not part of the "transientState" collection. + const { data, wooSettings, ...transientState } = getState( state ); return transientState || EMPTY_OBJ; }; diff --git a/modules/ppcp-settings/src/Data/CommonSettings.php b/modules/ppcp-settings/src/Data/CommonSettings.php index f49e2895b..b377b66aa 100644 --- a/modules/ppcp-settings/src/Data/CommonSettings.php +++ b/modules/ppcp-settings/src/Data/CommonSettings.php @@ -39,7 +39,7 @@ class CommonSettings extends AbstractDataModel { /** * Constructor. * - * @param string $country WooCommerce store country. + * @param string $country WooCommerce store country. * @param string $currency WooCommerce store currency. */ public function __construct( string $country, string $currency ) { @@ -137,7 +137,7 @@ public function set_client_secret( string $client_secret ) : void { } /** - * Returns the list of read-only customization flags + * Returns the list of read-only customization flags. * * @return array */ diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index bebf9980c..721c07e11 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -117,7 +117,7 @@ public function register_routes() { * * @return WP_REST_Response The common settings. */ - public function get_details(): WP_REST_Response { + public function get_details() : WP_REST_Response { $js_data = $this->sanitize_for_javascript( $this->settings->to_array(), $this->field_map From f570391387a7b2facd44a7746a8239ff65a8214e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 15:51:26 +0100 Subject: [PATCH 07/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20step-decision?= =?UTF-8?q?=20logic=20to=20helper=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/Onboarding.js | 22 +++++-------------- .../Screens/Onboarding/availableSteps.js | 13 ++++++++++- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index 30cd52ffe..ad6ca4677 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -1,24 +1,14 @@ import Container from '../../ReusableComponents/Container'; import { OnboardingHooks } from '../../../data'; -import { getSteps } from './availableSteps'; -import Navigation from './Components/Navigation'; - -const getCurrentStep = ( requestedStep, steps ) => { - const isValidStep = ( step ) => - typeof step === 'number' && - Number.isInteger( step ) && - step >= 0 && - step < steps.length; - const safeCurrentStep = isValidStep( requestedStep ) ? requestedStep : 0; - return steps[ safeCurrentStep ]; -}; +import { getSteps, getCurrentStep } from './availableSteps'; +import Navigation from './Components/Navigation'; const Onboarding = () => { const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); - const steps = getSteps( flags ); - const CurrentStepComponent = getCurrentStep( step, steps ); + const Steps = getSteps( flags ); + const CurrentStepComponent = getCurrentStep( step, Steps ); return ( <> @@ -26,7 +16,7 @@ const Onboarding = () => { setStep={ setStep } currentStep={ step } setCompleted={ setCompleted } - stepperOrder={ steps } + stepperOrder={ Steps } />
@@ -34,7 +24,7 @@ const Onboarding = () => { setStep={ setStep } currentStep={ step } setCompleted={ setCompleted } - stepperOrder={ steps } + stepperOrder={ Steps } />
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js index 7e8ea1556..15b0766d7 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js @@ -9,7 +9,7 @@ export const getSteps = ( flags ) => { StepWelcome, StepBusiness, StepProducts, - StepPaymentMethods, + StepPaymentMethods, StepCompleteSetup, ]; @@ -19,3 +19,14 @@ export const getSteps = ( flags ) => { return allSteps; }; + +export const getCurrentStep = ( requestedStep, steps ) => { + const isValidStep = ( step ) => + typeof step === 'number' && + Number.isInteger( step ) && + step >= 0 && + step < steps.length; + + const safeCurrentStep = isValidStep( requestedStep ) ? requestedStep : 0; + return steps[ safeCurrentStep ]; +}; From d08fe0dc333d225432948fe3e50bd0e0622789c8 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 16:40:51 +0100 Subject: [PATCH 08/93] =?UTF-8?q?=F0=9F=90=9B=20Add=20missing=20`break`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/Screens/Onboarding/Components/Navigation.js | 1 + .../resources/js/Components/Screens/Onboarding/StepBusiness.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index 5a1da25cb..e4c5ec3bc 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -48,6 +48,7 @@ const Navigation = ( { setStep, setCompleted, currentStep, stepperOrder } ) => { 'Choose checkout options', 'woocommerce-paypal-payments' ); + break; case 4: navigationTitle = __( 'Connect your PayPal account', diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js index a223686ff..dd9a1dcd5 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js @@ -10,9 +10,8 @@ const BUSINESS_RADIO_GROUP_NAME = 'business'; const StepBusiness = ( {} ) => { const { isCasualSeller, setIsCasualSeller } = OnboardingHooks.useBusiness(); - const handleSellerTypeChange = ( value ) => { + const handleSellerTypeChange = ( value ) => setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === value ); - }; const getCurrentValue = () => { if ( isCasualSeller === null ) { From cc782ad6b91f7046dcefa0ac3a2b346d4eae147f Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 17:02:08 +0100 Subject: [PATCH 09/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20naviga?= =?UTF-8?q?tion=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Onboarding/Components/Navigation.js | 38 +++--------- .../Screens/Onboarding/Onboarding.js | 5 +- .../Screens/Onboarding/availableSteps.js | 59 +++++++++++++++---- 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index e4c5ec3bc..54fc5a0f9 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -4,7 +4,13 @@ import { __ } from '@wordpress/i18n'; import { OnboardingHooks } from '../../../../data'; import data from '../../../../utils/data'; -const Navigation = ( { setStep, setCompleted, currentStep, stepperOrder } ) => { +const Navigation = ( { + setStep, + setCompleted, + currentStep, + stepperOrder, + title, +} ) => { const isLastStep = () => currentStep + 1 === stepperOrder.length; const isFistStep = () => currentStep === 0; const navigateBy = ( stepDirection ) => { @@ -25,41 +31,15 @@ const Navigation = ( { setStep, setCompleted, currentStep, stepperOrder } ) => { const { products } = OnboardingHooks.useProducts(); const { isCasualSeller } = OnboardingHooks.useBusiness(); - let navigationTitle = ''; let disabled = false; switch ( currentStep ) { case 1: - navigationTitle = __( - 'Set up store type', - 'woocommerce-paypal-payments' - ); disabled = isCasualSeller === null; break; case 2: - navigationTitle = __( - 'Select product types', - 'woocommerce-paypal-payments' - ); disabled = products.length < 1; break; - case 3: - navigationTitle = __( - 'Choose checkout options', - 'woocommerce-paypal-payments' - ); - break; - case 4: - navigationTitle = __( - 'Connect your PayPal account', - 'woocommerce-paypal-payments' - ); - break; - default: - navigationTitle = __( - 'PayPal Payments', - 'woocommerce-paypal-payments' - ); } return ( @@ -72,7 +52,7 @@ const Navigation = ( { setStep, setCompleted, currentStep, stepperOrder } ) => { variant="tertiary" onClick={ () => navigateBy( -1 ) } > - { navigationTitle } + { title } ) : ( { 'woocommerce-paypal-payments' ) } > - { navigationTitle } + { title } ) }
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index ad6ca4677..eb6676b13 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -8,7 +8,7 @@ const Onboarding = () => { const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); const Steps = getSteps( flags ); - const CurrentStepComponent = getCurrentStep( step, Steps ); + const { StepComponent, title } = getCurrentStep( step, Steps ); return ( <> @@ -17,10 +17,11 @@ const Onboarding = () => { currentStep={ step } setCompleted={ setCompleted } stepperOrder={ Steps } + title={ title } />
- { - const allSteps = [ - StepWelcome, - StepBusiness, - StepProducts, - StepPaymentMethods, - StepCompleteSetup, - ]; +/** + * List of all onboarding screens that are available. + * + * The screens are displayed in the order in which they appear in this array + * + * @type {[{id, StepComponent, title}]} + */ +const ALL_STEPS = [ + { + id: 'welcome', + title: __( 'PayPal Payments', 'woocommerce-paypal-payments' ), + StepComponent: StepWelcome, + }, + { + id: 'business', + title: __( 'Set up store type', 'woocommerce-paypal-payments' ), + StepComponent: StepBusiness, + }, + { + id: 'products', + title: __( 'Select product types', 'woocommerce-paypal-payments' ), + StepComponent: StepProducts, + }, + { + id: 'methods', + title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ), + StepComponent: StepPaymentMethods, + }, + { + id: 'complete', + title: __( + 'Connect your PayPal account', + 'woocommerce-paypal-payments' + ), + StepComponent: StepCompleteSetup, + }, +]; +export const getSteps = ( flags ) => { if ( ! flags.canUseCasualSelling ) { - return allSteps.filter( ( step ) => step !== StepBusiness ); + return ALL_STEPS.filter( ( step ) => 'business' !== step.id ); } - return allSteps; + return ALL_STEPS; }; +/** + * Returns the screen-details of the current step, based on the numeric step-index. + * + * @param {number} requestedStep Index of the screen to display. + * @param {[]} steps List of all available steps (see `getSteps()`) + * @return {{id, StepComponent, title}} The requested screen details, or the first welcome screen. + */ export const getCurrentStep = ( requestedStep, steps ) => { const isValidStep = ( step ) => typeof step === 'number' && From 14e3b1077afd7676e90d8d5a3c745eb605a3a9ff Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 17:43:30 +0100 Subject: [PATCH 10/93] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20cleanup=20of=20scs?= =?UTF-8?q?s=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/reusable-components/_navigation.scss | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 09be265c1..5c7c6804f 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -22,12 +22,15 @@ @include font(16, 24, 600); color: $color-gray-900; &:hover{ - background-color:none; - background:none; + background-color: transparent; + background: none; } } &--left { + align-items: center; + display: inline-flex; + &__link { @include font(20, 28, 400); color: $color-gray-900; @@ -44,15 +47,17 @@ &--progress-bar { position: absolute; - bottom: 0px; + bottom: 0; left: 0; background-color: $color-blueberry; height: 4px; + transition: width 0.3s; } } @media screen and (max-width: 480px) { padding: 24px 35px; + .ppcp-r-navigation { flex-wrap: wrap; row-gap: 8px; From aef5119ecd6a026c170bd916cedfabb20d23a1ce Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 17:44:11 +0100 Subject: [PATCH 11/93] =?UTF-8?q?=E2=9C=A8=20Consolidate=20navigation=20lo?= =?UTF-8?q?gic=20further?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/availableSteps.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js index 9797a686e..e14e66231 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js @@ -18,21 +18,25 @@ const ALL_STEPS = [ id: 'welcome', title: __( 'PayPal Payments', 'woocommerce-paypal-payments' ), StepComponent: StepWelcome, + canProceed: () => true, }, { id: 'business', title: __( 'Set up store type', 'woocommerce-paypal-payments' ), StepComponent: StepBusiness, + canProceed: ( { business } ) => business.isCasualSeller !== null, }, { id: 'products', title: __( 'Select product types', 'woocommerce-paypal-payments' ), StepComponent: StepProducts, + canProceed: ( { products } ) => products.products.length > 0, }, { id: 'methods', title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ), StepComponent: StepPaymentMethods, + canProceed: () => true, }, { id: 'complete', @@ -41,15 +45,26 @@ const ALL_STEPS = [ 'woocommerce-paypal-payments' ), StepComponent: StepCompleteSetup, + canProceed: () => true, }, ]; export const getSteps = ( flags ) => { - if ( ! flags.canUseCasualSelling ) { - return ALL_STEPS.filter( ( step ) => 'business' !== step.id ); - } + const steps = flags.canUseCasualSelling + ? ALL_STEPS + : ALL_STEPS.filter( ( step ) => step.id !== 'business' ); - return ALL_STEPS; + const totalStepsCount = steps.length; + + return steps.map( ( step, index ) => ( { + ...step, + isFirst: index === 0, + isLast: index === totalStepsCount - 1, + showNext: index < totalStepsCount - 1, + percentage: 100 * ( index / ( totalStepsCount - 1 ) ), + nextStep: index < totalStepsCount - 1 ? index + 1 : index, + prevStep: index > 0 ? index - 1 : 0, + } ) ); }; /** From 2b878373451ccf061b383a7a3ef28e908e4e6af6 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 17:45:36 +0100 Subject: [PATCH 12/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20real=20icon=20?= =?UTF-8?q?instead=20of=20data().getImage()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Screens/Onboarding/Components/Navigation.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index 54fc5a0f9..2d63d6f06 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -1,8 +1,8 @@ -import { Button } from '@wordpress/components'; +import { Button, Icon } from '@wordpress/components'; +import { chevronLeft } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { OnboardingHooks } from '../../../../data'; -import data from '../../../../utils/data'; const Navigation = ( { setStep, @@ -46,7 +46,7 @@ const Navigation = ( {
- { data().getImage( 'icon-arrow-left.svg' ) } + { ! isFistStep() ? ( - ) : ( - - { title } - - ) } +
- { ! isFistStep() && ( -
- - { __( - 'Save and exit', - 'woocommerce-paypal-payments' - ) } - - { ! isLastStep() && ( - - ) } -
- ) } -
+ { ! isFirst && + NextButton( { showNext, isDisabled, onNext, onExit } ) } +
); }; +const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => { + return ( +
+ + { showNext && ( + + ) } +
+ ); +}; + +const ProgressBar = ( { percent } ) => { + percent = Math.min( Math.max( percent, 0 ), 100 ); + + return ( +
+ ); +}; + export default Navigation; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index eb6676b13..e59bcdeeb 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -8,20 +8,26 @@ const Onboarding = () => { const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); const Steps = getSteps( flags ); - const { StepComponent, title } = getCurrentStep( step, Steps ); + const currentStep = getCurrentStep( step, Steps ); + + const handleNext = () => setStep( currentStep.nextStep ); + const handlePrev = () => setStep( currentStep.prevStep ); + const handleExit = () => { + window.location.href = window.ppcpSettings.wcPaymentsTabUrl; + }; return ( <> +
- { return { flags, isReady, step, setStep, completed, setCompleted }; }; + +export const useNavigationState = () => { + const products = useProducts(); + const business = useBusiness(); + + return { + products, + business, + }; +}; From 36b51d2b2a0582e8e58c6465202a4f3028e1affe Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 18:25:15 +0100 Subject: [PATCH 14/93] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20SCSS=20for=20hea?= =?UTF-8?q?der=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reusable-components/_navigation.scss | 65 +++++++++++-------- .../Onboarding/Components/Navigation.js | 11 ++-- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 5c7c6804f..5dd1e21b9 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -4,52 +4,63 @@ border-bottom: 1px solid $color-gray-300; position: relative; + --color-accent: #{$color-blueberry}; + --color-text: #{$color-gray-900}; + --color-disabled: #CCC; + .ppcp-r-navigation { display: flex; justify-content: space-between; align-items: center; + height: 40px; + + .components-button { + @include font(13, 20, 400); - button.is-primary { - padding: 10px 18px; - justify-content: center; - margin: 0 0 0 12px; - &:not(:disabled) { - background-color: $color-blueberry; + &.is-primary { + background-color: var(--color-accent); + padding: 10px 18px; + justify-content: center; + margin: 0 0 0 12px; + + &:disabled { + background-color: var(--color-disabled); + } } - } - button.is-tertiary { - @include font(16, 24, 600); - color: $color-gray-900; - &:hover{ - background-color: transparent; - background: none; + &.is-link { + color: var(--color-accent); + text-decoration: none; + + &:disabled { + color: var(--color-disabled); + } } - } - &--left { - align-items: center; - display: inline-flex; + &.is-title { + @include font(16, 24, 600); + color: var(--color-text); - &__link { - @include font(20, 28, 400); - color: $color-gray-900; - text-decoration: none; - padding: 0 0 0 18px; + .title { + margin-left: 18px; + } + + .big { + @include font(20, 28, 400); + } } } - &--right a{ - @include font(13, 20, 400); - color: $color-blueberry; - text-decoration: none; + &--left { + align-items: center; + display: inline-flex; } &--progress-bar { position: absolute; bottom: 0; left: 0; - background-color: $color-blueberry; + background-color: var(--color-accent); height: 4px; transition: width 0.3s; } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index 74dd4bc46..b31dca26a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -14,12 +14,15 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
-
{ ! isFirst && @@ -33,7 +36,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => { const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => { return (
- { showNext && ( From aabb551e6e489277beab565b6782da68e6475772 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 18:38:43 +0100 Subject: [PATCH 15/93] =?UTF-8?q?=F0=9F=92=84=20Sticky=20nav=20bar,=20cons?= =?UTF-8?q?olidate=20repeat=20styles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/css/_variables.scss | 4 ++++ .../reusable-components/_navigation.scss | 19 ++++++++++++------- .../css/components/screens/_fullscreen.scss | 17 +++++++++++++++++ .../screens/_onboarding-global.scss | 8 -------- .../components/screens/_settings-global.scss | 7 ------- .../ppcp-settings/resources/css/style.scss | 3 +-- 6 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss delete mode 100644 modules/ppcp-settings/resources/css/components/screens/_onboarding-global.scss delete mode 100644 modules/ppcp-settings/resources/css/components/screens/_settings-global.scss diff --git a/modules/ppcp-settings/resources/css/_variables.scss b/modules/ppcp-settings/resources/css/_variables.scss index 613403b67..71b4cae6e 100644 --- a/modules/ppcp-settings/resources/css/_variables.scss +++ b/modules/ppcp-settings/resources/css/_variables.scss @@ -24,6 +24,10 @@ $max-width-onboarding: 1024px; $max-width-onboarding-content: 500px; $max-width-settings: 938px; +:root { + --ppcp-color-app-bg: #{$color-white}; +} + #ppcp-settings-container { --max-width-settings: #{$max-width-settings}; --max-width-onboarding: #{$max-width-onboarding}; diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 5dd1e21b9..38df93594 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -1,10 +1,15 @@ .ppcp-r-navigation-container { - padding: 24px 48px; + position: sticky; + top: var(--wp-admin--admin-bar--height); + z-index: 10; + + padding: 10px 48px; margin: 0 -20px 48px -20px; - border-bottom: 1px solid $color-gray-300; - position: relative; - --color-accent: #{$color-blueberry}; + box-shadow: 0 -1px 0 0 $color-gray-300 inset; + background: var(--ppcp-color-app-bg); + + --wp-components-color-accent: #{$color-blueberry}; --color-text: #{$color-gray-900}; --color-disabled: #CCC; @@ -18,7 +23,7 @@ @include font(13, 20, 400); &.is-primary { - background-color: var(--color-accent); + background-color: var(--wp-components-color-accent); padding: 10px 18px; justify-content: center; margin: 0 0 0 12px; @@ -29,7 +34,7 @@ } &.is-link { - color: var(--color-accent); + color: var(--wp-components-color-accent); text-decoration: none; &:disabled { @@ -60,7 +65,7 @@ position: absolute; bottom: 0; left: 0; - background-color: var(--color-accent); + background-color: var(--wp-components-color-accent); height: 4px; transition: width 0.3s; } diff --git a/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss b/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss new file mode 100644 index 000000000..41568fbc2 --- /dev/null +++ b/modules/ppcp-settings/resources/css/components/screens/_fullscreen.scss @@ -0,0 +1,17 @@ +body:has(.ppcp-r-container--settings), +body:has(.ppcp-r-container--onboarding) { + background-color: var(--ppcp-color-app-bg) !important; + + .woocommerce-layout { + padding: 0 !important; + } + + .notice, + .nav-tab-wrapper.woo-nav-tab-wrapper, + .woocommerce-layout__header, + .wrap.woocommerce form > h2, + #screen-meta-links { + display: none !important; + visibility: hidden; + } +} diff --git a/modules/ppcp-settings/resources/css/components/screens/_onboarding-global.scss b/modules/ppcp-settings/resources/css/components/screens/_onboarding-global.scss deleted file mode 100644 index 7878ef729..000000000 --- a/modules/ppcp-settings/resources/css/components/screens/_onboarding-global.scss +++ /dev/null @@ -1,8 +0,0 @@ -body:has(.ppcp-r-container--onboarding) { - background-color: #fff !important; - - .notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout__header, .wrap.woocommerce form > h2, #screen-meta-links { - display: none !important; - visibility: hidden; - } -} diff --git a/modules/ppcp-settings/resources/css/components/screens/_settings-global.scss b/modules/ppcp-settings/resources/css/components/screens/_settings-global.scss deleted file mode 100644 index 629d89d76..000000000 --- a/modules/ppcp-settings/resources/css/components/screens/_settings-global.scss +++ /dev/null @@ -1,7 +0,0 @@ -body:has(.ppcp-r-container--settings) { - background-color: #fff !important; - - .notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout, .wrap.woocommerce form > h2, #screen-meta-links { - display: none !important; - } -} diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index a1f5b390b..8bf0cca40 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -28,5 +28,4 @@ } @import './components/reusable-components/payment-method-modal'; -@import './components/screens/onboarding-global'; -@import './components/screens/settings-global'; +@import './components/screens/fullscreen'; From 0f992dbe3b4b6b56c38b7c8914891610710b549c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 2 Dec 2024 18:44:49 +0100 Subject: [PATCH 16/93] =?UTF-8?q?=F0=9F=92=84=20Adjust=20paddings=20and=20?= =?UTF-8?q?border=20radius?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../css/components/reusable-components/_navigation.scss | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 38df93594..311c22e7d 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -24,9 +24,10 @@ &.is-primary { background-color: var(--wp-components-color-accent); - padding: 10px 18px; + padding: 10px 16px; justify-content: center; margin: 0 0 0 12px; + border-radius: 2px; &:disabled { background-color: var(--color-disabled); @@ -61,6 +62,12 @@ display: inline-flex; } + &--right { + .is-link { + padding: 10px 16px; + } + } + &--progress-bar { position: absolute; bottom: 0; From 841a98e0090f06632f1e58b4609d149cd2166cf0 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 15:18:08 +0100 Subject: [PATCH 17/93] =?UTF-8?q?=E2=9C=A8=20Add=20scroll-effect=20(shadow?= =?UTF-8?q?)=20to=20header=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reusable-components/_navigation.scss | 5 +++ .../Onboarding/Components/Navigation.js | 9 +++- .../resources/js/hooks/useIsScrolled.js | 44 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 modules/ppcp-settings/resources/js/hooks/useIsScrolled.js diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss index 311c22e7d..e149026cf 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_navigation.scss @@ -8,6 +8,7 @@ box-shadow: 0 -1px 0 0 $color-gray-300 inset; background: var(--ppcp-color-app-bg); + transition: box-shadow 0.3s; --wp-components-color-accent: #{$color-blueberry}; --color-text: #{$color-gray-900}; @@ -78,6 +79,10 @@ } } + &.is-scrolled { + box-shadow: 0 -1px 0 0 $color-gray-300 inset, 0 8px 8px 0 rgba(85,93,102,.3); + } + @media screen and (max-width: 480px) { padding: 24px 35px; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index b31dca26a..82bfdb656 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -2,16 +2,23 @@ import { Button, Icon } from '@wordpress/components'; import { chevronLeft } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; +import classNames from 'classnames'; + import { OnboardingHooks } from '../../../../data'; +import useIsScrolled from '../../../../hooks/useIsScrolled'; const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => { const { title, isFirst, percentage, showNext, canProceed } = stepDetails; + const { isScrolled } = useIsScrolled(); const state = OnboardingHooks.useNavigationState(); const isDisabled = ! canProceed( state ); + const className = classNames( 'ppcp-r-navigation-container', { + 'is-scrolled': isScrolled, + } ); return ( -
+
+ { } value={ clientId } onChange={ setClientId } - className={ - clientId && ! isValidClientId ? 'has-error' : '' - } /> - { clientId && ! isValidClientId && ( -

- { __( - 'Please enter a valid Client ID', - 'woocommerce-paypal-payments' - ) } -

- ) } { onChange={ setClientSecret } type="password" /> -
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js new file mode 100644 index 000000000..179c8773f --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -0,0 +1,36 @@ +import { Button } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { openSignup } from '../../../ReusableComponents/Icons'; +import { useSandboxConnection } from '../../../../hooks/useHandleConnections'; + +const ConnectionButton = ( { + title, + isSandbox = false, + variant = 'primary', + showIcon = true, +} ) => { + const className = 'ppcp-r-connection-button'; + const { handleSandboxConnect } = useSandboxConnection(); + + const handleConnectClick = () => { + if ( isSandbox ) { + handleSandboxConnect(); + } else { + // Handle live connection logic here + console.warn( 'Live connection not implemented yet' ); + } + }; + + return ( + + ); +}; + +export default ConnectionButton; diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js new file mode 100644 index 000000000..b30d5213f --- /dev/null +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -0,0 +1,113 @@ +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; + +import { CommonHooks, OnboardingHooks } from '../data'; +import { openPopup } from '../utils/window'; + +const useCommonConnectionLogic = () => { + const { setCompleted } = OnboardingHooks.useSteps(); + const { createSuccessNotice, createErrorNotice } = + useDispatch( noticesStore ); + + const handleServerError = ( res, genericMessage ) => { + console.error( 'Connection error', res ); + createErrorNotice( res?.message ?? genericMessage ); + }; + + const handleServerSuccess = () => { + createSuccessNotice( + __( 'Connected to PayPal', 'woocommerce-paypal-payments' ) + ); + setCompleted( true ); + }; + + return { handleServerError, handleServerSuccess, createErrorNotice }; +}; + +export const useSandboxConnection = () => { + const { connectViaSandbox, isSandboxMode, setSandboxMode } = + CommonHooks.useSandbox(); + const { handleServerError, createErrorNotice } = useCommonConnectionLogic(); + + const handleSandboxConnect = async () => { + const res = await connectViaSandbox(); + + if ( ! res.success || ! res.data ) { + handleServerError( + res, + __( + 'Could not generate a Sandbox login link.', + 'woocommerce-paypal-payments' + ) + ); + return; + } + + const connectionUrl = res.data; + const popup = openPopup( connectionUrl ); + + if ( ! popup ) { + createErrorNotice( + __( + 'Popup blocked. Please allow popups for this site to connect to PayPal.', + 'woocommerce-paypal-payments' + ) + ); + } + }; + + return { + handleSandboxConnect, + isSandboxMode, + setSandboxMode, + }; +}; + +export const useManualConnection = () => { + const { + connectViaIdAndSecret, + isManualConnectionMode, + setManualConnectionMode, + clientId, + setClientId, + clientSecret, + setClientSecret, + } = CommonHooks.useManualConnection(); + const { handleServerError, handleServerSuccess, createErrorNotice } = + useCommonConnectionLogic(); + + const handleConnectViaIdAndSecret = async ( { validation } = {} ) => { + if ( 'function' === typeof validation ) { + try { + validation(); + } catch ( exception ) { + createErrorNotice( exception.message ); + return; + } + } + const res = await connectViaIdAndSecret(); + + if ( res.success ) { + handleServerSuccess(); + } else { + handleServerError( + res, + __( + 'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.', + 'woocommerce-paypal-payments' + ) + ); + } + }; + + return { + handleConnectViaIdAndSecret, + isManualConnectionMode, + setManualConnectionMode, + clientId, + setClientId, + clientSecret, + setClientSecret, + }; +}; From a58a2c7b209d274b1762080019095bdf67396199 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 16:06:41 +0100 Subject: [PATCH 22/93] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20setCompl?= =?UTF-8?q?eted=20prop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js --- .../Components/Screens/Onboarding/Onboarding.js | 4 +--- .../Screens/Onboarding/StepCompleteSetup.js | 17 +++++------------ .../Screens/Onboarding/StepWelcome.js | 5 +++-- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index e59bcdeeb..225527053 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -5,8 +5,7 @@ import { getSteps, getCurrentStep } from './availableSteps'; import Navigation from './Components/Navigation'; const Onboarding = () => { - const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); - + const { step, setStep, flags } = OnboardingHooks.useSteps(); const Steps = getSteps( flags ); const currentStep = getCurrentStep( step, Steps ); @@ -30,7 +29,6 @@ const Onboarding = () => {
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js index d649b7c83..ed2001ac2 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepCompleteSetup.js @@ -1,10 +1,9 @@ import { __ } from '@wordpress/i18n'; -import { Button } from '@wordpress/components'; import OnboardingHeader from '../../ReusableComponents/OnboardingHeader'; -import { openSignup } from '../../ReusableComponents/Icons'; +import ConnectionButton from './Components/ConnectionButton'; -const StepCompleteSetup = ( { setCompleted } ) => { +const StepCompleteSetup = () => { return (
{ />
-
diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js index 761093b24..461d95d26 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -10,8 +10,9 @@ import AccordionSection from '../../ReusableComponents/AccordionSection'; import AdvancedOptionsForm from './Components/AdvancedOptionsForm'; import { CommonHooks } from '../../../data'; -const StepWelcome = ( { setStep, currentStep, setCompleted } ) => { +const StepWelcome = ( { setStep, currentStep } ) => { const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + return (
{ className="onboarding-advanced-options" id="advanced-options" > - +
); From 2ccc489fd6e5e5a1d27b90573523cc8ee41630f5 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 16:19:16 +0100 Subject: [PATCH 23/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor=20code=20impro?= =?UTF-8?q?vement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Onboarding/Components/ConnectionButton.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js index 179c8773f..8fb4b235c 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -1,5 +1,7 @@ import { Button } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; + +import classNames from 'classnames'; + import { openSignup } from '../../../ReusableComponents/Icons'; import { useSandboxConnection } from '../../../../hooks/useHandleConnections'; @@ -9,14 +11,16 @@ const ConnectionButton = ( { variant = 'primary', showIcon = true, } ) => { - const className = 'ppcp-r-connection-button'; const { handleSandboxConnect } = useSandboxConnection(); + const className = classNames( 'ppcp-r-connection-button', { + 'sandbox-mode': isSandbox, + 'live-mode': ! isSandbox, + } ); - const handleConnectClick = () => { + const handleConnectClick = async () => { if ( isSandbox ) { - handleSandboxConnect(); + await handleSandboxConnect(); } else { - // Handle live connection logic here console.warn( 'Live connection not implemented yet' ); } }; From 8341d4ec0e0f5f5d19fd6b74b7caa795f03593b7 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 16:28:28 +0100 Subject: [PATCH 24/93] =?UTF-8?q?=E2=9C=A8=20First=20draft=20of=20producti?= =?UTF-8?q?on=20login=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/action-types.js | 1 + .../resources/js/data/common/actions.js | 17 +++++++++++ .../resources/js/data/common/constants.js | 4 +-- .../resources/js/data/common/controls.js | 28 +++++++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js index 47de76afe..3b1e1fcf3 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -16,4 +16,5 @@ export default { DO_PERSIST_DATA: 'COMMON:DO_PERSIST_DATA', DO_MANUAL_CONNECTION: 'COMMON:DO_MANUAL_CONNECTION', DO_SANDBOX_LOGIN: 'COMMON:DO_SANDBOX_LOGIN', + DO_PRODUCTION_LOGIN: 'COMMON:DO_PRODUCTION_LOGIN', }; diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 619aaca5f..ad9a8f5b5 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -131,6 +131,23 @@ export const connectViaSandbox = function* () { return result; }; +/** + * Side effect. Initiates the production login ISU. + * + * @return {Action} The action. + */ +export const connectToProduction = function* () { + yield setIsBusy( true ); + + const result = yield { + type: ACTION_TYPES.DO_PRODUCTION_LOGIN, + products: [ 'EXPRESS_CHECKOUT' ], + }; + yield setIsBusy( false ); + + return result; +}; + /** * Side effect. Initiates a manual connection attempt using the provided client ID and secret. * diff --git a/modules/ppcp-settings/resources/js/data/common/constants.js b/modules/ppcp-settings/resources/js/data/common/constants.js index c7ea9b4c1..4ec4ad20d 100644 --- a/modules/ppcp-settings/resources/js/data/common/constants.js +++ b/modules/ppcp-settings/resources/js/data/common/constants.js @@ -36,11 +36,11 @@ export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/common'; export const REST_MANUAL_CONNECTION_PATH = '/wc/v3/wc_paypal/connect_manual'; /** - * REST path to generate an ISU URL for the sandbox-login. + * REST path to generate an ISU URL for the PayPal-login. * * Used by: Controls * See: LoginLinkRestEndpoint.php * * @type {string} */ -export const REST_SANDBOX_CONNECTION_PATH = '/wc/v3/wc_paypal/login_link'; +export const REST_CONNECTION_URL_PATH = '/wc/v3/wc_paypal/login_link'; diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js index 6de513e0b..6005385f9 100644 --- a/modules/ppcp-settings/resources/js/data/common/controls.js +++ b/modules/ppcp-settings/resources/js/data/common/controls.js @@ -12,7 +12,7 @@ import apiFetch from '@wordpress/api-fetch'; import { REST_PERSIST_PATH, REST_MANUAL_CONNECTION_PATH, - REST_SANDBOX_CONNECTION_PATH, + REST_CONNECTION_URL_PATH, } from './constants'; import ACTION_TYPES from './action-types'; @@ -34,11 +34,33 @@ export const controls = { try { result = await apiFetch( { - path: REST_SANDBOX_CONNECTION_PATH, + path: REST_CONNECTION_URL_PATH, method: 'POST', data: { environment: 'sandbox', - products: [ 'EXPRESS_CHECKOUT' ], + products: [ 'EXPRESS_CHECKOUT' ], // Sandbox always uses EXPRESS_CHECKOUT. + }, + } ); + } catch ( e ) { + result = { + success: false, + error: e, + }; + } + + return result; + }, + + async [ ACTION_TYPES.DO_PRODUCTION_LOGIN ]( { products } ) { + let result = null; + + try { + result = await apiFetch( { + path: REST_CONNECTION_URL_PATH, + method: 'POST', + data: { + environment: 'production', + products, }, } ); } catch ( e ) { From 907567992145e371ecb43164186e45df27cffd5b Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 17:54:09 +0100 Subject: [PATCH 25/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20Redux=20act?= =?UTF-8?q?ion=20for=20consistent=20naming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/resources/js/data/common/actions.js | 7 ++++--- modules/ppcp-settings/resources/js/data/common/hooks.js | 8 ++++---- .../resources/js/hooks/useHandleConnections.js | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index ad9a8f5b5..6b1a03b4e 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -122,7 +122,7 @@ export const persist = function* () { * * @return {Action} The action. */ -export const connectViaSandbox = function* () { +export const connectToSandbox = function* () { yield setIsBusy( true ); const result = yield { type: ACTION_TYPES.DO_SANDBOX_LOGIN }; @@ -134,14 +134,15 @@ export const connectViaSandbox = function* () { /** * Side effect. Initiates the production login ISU. * + * @param {string[]} products Which products/features to display in the ISU popup. * @return {Action} The action. */ -export const connectToProduction = function* () { +export const connectToProduction = function* ( products = [] ) { yield setIsBusy( true ); const result = yield { type: ACTION_TYPES.DO_PRODUCTION_LOGIN, - products: [ 'EXPRESS_CHECKOUT' ], + products, }; yield setIsBusy( false ); diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index fbe6a4842..3f9185102 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -31,7 +31,7 @@ const useHooks = () => { setManualConnectionMode, setClientId, setClientSecret, - connectViaSandbox, + connectToSandbox, connectViaIdAndSecret, } = useDispatch( STORE_NAME ); @@ -72,7 +72,7 @@ const useHooks = () => { setClientSecret: ( value ) => { return savePersistent( setClientSecret, value ); }, - connectViaSandbox, + connectToSandbox, connectViaIdAndSecret, wooSettings, }; @@ -89,9 +89,9 @@ export const useBusyState = () => { }; export const useSandbox = () => { - const { isSandboxMode, setSandboxMode, connectViaSandbox } = useHooks(); + const { isSandboxMode, setSandboxMode, connectToSandbox } = useHooks(); - return { isSandboxMode, setSandboxMode, connectViaSandbox }; + return { isSandboxMode, setSandboxMode, connectToSandbox }; }; export const useManualConnection = () => { diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index b30d5213f..e9a2e30bc 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -26,12 +26,12 @@ const useCommonConnectionLogic = () => { }; export const useSandboxConnection = () => { - const { connectViaSandbox, isSandboxMode, setSandboxMode } = + const { connectToSandbox, isSandboxMode, setSandboxMode } = CommonHooks.useSandbox(); const { handleServerError, createErrorNotice } = useCommonConnectionLogic(); const handleSandboxConnect = async () => { - const res = await connectViaSandbox(); + const res = await connectToSandbox(); if ( ! res.success || ! res.data ) { handleServerError( From 74edbc7184c728d86e66517330678702724526c4 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 18:31:35 +0100 Subject: [PATCH 26/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20validatio?= =?UTF-8?q?n=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js --- .../Components/AdvancedOptionsForm.js | 64 ++++++++----------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index 731436df5..1e2c0ac97 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -1,6 +1,8 @@ import { __, sprintf } from '@wordpress/i18n'; import { Button, TextControl } from '@wordpress/components'; -import { useRef } from '@wordpress/element'; +import { useRef, useState, useEffect } from '@wordpress/element'; + +import classNames from 'classnames'; import SettingsToggleBlock from '../../../ReusableComponents/SettingsToggleBlock'; import Separator from '../../../ReusableComponents/Separator'; @@ -14,6 +16,8 @@ import { import ConnectionButton from './ConnectionButton'; const AdvancedOptionsForm = () => { + const [ clientValid, setClientValid ] = useState( false ); + const [ secretValid, setSecretValid ] = useState( false ); const { isBusy } = CommonHooks.useBusyState(); const { isSandboxMode, setSandboxMode } = useSandboxConnection(); const { @@ -29,43 +33,10 @@ const AdvancedOptionsForm = () => { const refClientId = useRef( null ); const refClientSecret = useRef( null ); - const validateManualConnectionForm = () => { - const fields = [ - { - ref: refClientId, - value: clientId, - errorMessage: __( - 'Please enter your Client ID', - 'woocommerce-paypal-payments' - ), - }, - { - ref: refClientSecret, - value: clientSecret, - errorMessage: __( - 'Please enter your Secret Key', - 'woocommerce-paypal-payments' - ), - }, - ]; - - for ( const { ref, value, errorMessage } of fields ) { - if ( value ) { - continue; - } - - ref?.current?.focus(); - throw new Error( errorMessage ); - } - - return true; - }; - - const handleManualConnect = async () => { - await handleConnectViaIdAndSecret( { - validation: validateManualConnectionForm, - } ); - }; + useEffect( () => { + setClientValid( ! clientId || /^A[\w-]{79}$/.test( clientId ) ); + setSecretValid( clientSecret && clientSecret.length > 0 ); + }, [ clientId, clientSecret ] ); const advancedUsersDescription = sprintf( // translators: %s: Link to PayPal REST application guide @@ -130,7 +101,18 @@ const AdvancedOptionsForm = () => { } value={ clientId } onChange={ setClientId } + className={ classNames( { + 'has-error': ! clientValid, + } ) } /> + { clientValid || ( +

+ { __( + 'Please enter a valid Client ID', + 'woocommerce-paypal-payments' + ) } +

+ ) } { onChange={ setClientSecret } type="password" /> - From 71347957d1709083e4c30b34a23ca3dda9e8f144 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 18:49:40 +0100 Subject: [PATCH 27/93] =?UTF-8?q?=F0=9F=9A=B8=20Restore=20previous=20error?= =?UTF-8?q?=20handling=20(additional)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index 1e2c0ac97..cf3eaa7cf 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -33,6 +33,43 @@ const AdvancedOptionsForm = () => { const refClientId = useRef( null ); const refClientSecret = useRef( null ); + + const validateManualConnectionForm = () => { + const fields = [ + { + ref: refClientId, + valid: () => clientId && clientValid, + errorMessage: __( + 'Please enter a valid Client ID', + 'woocommerce-paypal-payments' + ), + }, + { + ref: refClientSecret, + valid: () => clientSecret && secretValid, + errorMessage: __( + 'Please enter your Secret Key', + 'woocommerce-paypal-payments' + ), + }, + ]; + + for ( const { ref, valid, errorMessage } of fields ) { + if ( valid() ) { + continue; + } + + ref?.current?.focus(); + throw new Error( errorMessage ); + } + }; + + const handleManualConnect = async () => { + await handleConnectViaIdAndSecret( { + validation: validateManualConnectionForm, + } ); + }; + useEffect( () => { setClientValid( ! clientId || /^A[\w-]{79}$/.test( clientId ) ); setSecretValid( clientSecret && clientSecret.length > 0 ); @@ -131,11 +168,7 @@ const AdvancedOptionsForm = () => { onChange={ setClientSecret } type="password" /> - From 3ddff169e720b5b1984ebbc14fd038daea0a3850 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 3 Dec 2024 18:53:59 +0100 Subject: [PATCH 28/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20error?= =?UTF-8?q?=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index cf3eaa7cf..9875cc9a0 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -33,28 +33,41 @@ const AdvancedOptionsForm = () => { const refClientId = useRef( null ); const refClientSecret = useRef( null ); + const errors = { + noClientId: __( + 'Please enter a Client ID', + 'woocommerce-paypal-payments' + ), + noClientSecret: __( + 'Please enter your Secret Key', + 'woocommerce-paypal-payments' + ), + invalidClientId: __( + 'Please enter a valid Client ID', + 'woocommerce-paypal-payments' + ), + }; const validateManualConnectionForm = () => { - const fields = [ + const checks = [ { ref: refClientId, - valid: () => clientId && clientValid, - errorMessage: __( - 'Please enter a valid Client ID', - 'woocommerce-paypal-payments' - ), + valid: () => clientId, + errorMessage: errors.noClientId, + }, + { + ref: refClientId, + valid: () => clientValid, + errorMessage: errors.invalidClientId, }, { ref: refClientSecret, valid: () => clientSecret && secretValid, - errorMessage: __( - 'Please enter your Secret Key', - 'woocommerce-paypal-payments' - ), + errorMessage: errors.noClientSecret, }, ]; - for ( const { ref, valid, errorMessage } of fields ) { + for ( const { ref, valid, errorMessage } of checks ) { if ( valid() ) { continue; } @@ -144,10 +157,7 @@ const AdvancedOptionsForm = () => { /> { clientValid || (

- { __( - 'Please enter a valid Client ID', - 'woocommerce-paypal-payments' - ) } + { errors.invalidClientId }

) } Date: Tue, 3 Dec 2024 18:57:34 +0100 Subject: [PATCH 29/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20label=20lo?= =?UTF-8?q?gic=20to=20new=20effect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index 9875cc9a0..bad5cf953 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -18,6 +18,9 @@ import ConnectionButton from './ConnectionButton'; const AdvancedOptionsForm = () => { const [ clientValid, setClientValid ] = useState( false ); const [ secretValid, setSecretValid ] = useState( false ); + const [ clientIdLabel, setClientIdLabel ] = useState( '' ); + const [ secretKeyLabel, setSecretKeyLabel ] = useState( '' ); + const { isBusy } = CommonHooks.useBusyState(); const { isSandboxMode, setSandboxMode } = useSandboxConnection(); const { @@ -88,6 +91,19 @@ const AdvancedOptionsForm = () => { setSecretValid( clientSecret && clientSecret.length > 0 ); }, [ clientId, clientSecret ] ); + useEffect( () => { + setClientIdLabel( + isSandboxMode + ? __( 'Sandbox Client ID', 'woocommerce-paypal-payments' ) + : __( 'Live Client ID', 'woocommerce-paypal-payments' ) + ); + setSecretKeyLabel( + isSandboxMode + ? __( 'Sandbox Secret Key', 'woocommerce-paypal-payments' ) + : __( 'Live Secret Key', 'woocommerce-paypal-payments' ) + ); + }, [ isSandboxMode ] ); + const advancedUsersDescription = sprintf( // translators: %s: Link to PayPal REST application guide __( @@ -138,17 +154,7 @@ const AdvancedOptionsForm = () => { { Date: Tue, 3 Dec 2024 19:00:04 +0100 Subject: [PATCH 30/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor=20code=20impro?= =?UTF-8?q?vements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index bad5cf953..cc6f49ae2 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -15,6 +15,21 @@ import { import ConnectionButton from './ConnectionButton'; +const FORM_ERRORS = { + noClientId: __( + 'Please enter your Client ID', + 'woocommerce-paypal-payments' + ), + noClientSecret: __( + 'Please enter your Secret Key', + 'woocommerce-paypal-payments' + ), + invalidClientId: __( + 'Please enter a valid Client ID', + 'woocommerce-paypal-payments' + ), +}; + const AdvancedOptionsForm = () => { const [ clientValid, setClientValid ] = useState( false ); const [ secretValid, setSecretValid ] = useState( false ); @@ -36,37 +51,22 @@ const AdvancedOptionsForm = () => { const refClientId = useRef( null ); const refClientSecret = useRef( null ); - const errors = { - noClientId: __( - 'Please enter a Client ID', - 'woocommerce-paypal-payments' - ), - noClientSecret: __( - 'Please enter your Secret Key', - 'woocommerce-paypal-payments' - ), - invalidClientId: __( - 'Please enter a valid Client ID', - 'woocommerce-paypal-payments' - ), - }; - const validateManualConnectionForm = () => { const checks = [ { ref: refClientId, valid: () => clientId, - errorMessage: errors.noClientId, + errorMessage: FORM_ERRORS.noClientId, }, { ref: refClientId, valid: () => clientValid, - errorMessage: errors.invalidClientId, + errorMessage: FORM_ERRORS.invalidClientId, }, { ref: refClientSecret, valid: () => clientSecret && secretValid, - errorMessage: errors.noClientSecret, + errorMessage: FORM_ERRORS.noClientSecret, }, ]; @@ -80,11 +80,10 @@ const AdvancedOptionsForm = () => { } }; - const handleManualConnect = async () => { - await handleConnectViaIdAndSecret( { + const handleManualConnect = () => + handleConnectViaIdAndSecret( { validation: validateManualConnectionForm, } ); - }; useEffect( () => { setClientValid( ! clientId || /^A[\w-]{79}$/.test( clientId ) ); @@ -163,7 +162,7 @@ const AdvancedOptionsForm = () => { /> { clientValid || (

- { errors.invalidClientId } + { FORM_ERRORS.invalidClientId }

) } Date: Wed, 4 Dec 2024 18:36:35 +0400 Subject: [PATCH 31/93] Fix the welcome screen --- .../reusable-components/_badge-box.scss | 15 +++++++++++++++ .../reusable-components/_onboarding-header.scss | 4 ++++ .../reusable-components/_welcome-docs.scss | 3 +-- .../screens/onboarding/_step-welcome.scss | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_badge-box.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_badge-box.scss index 427b4b1fb..74fb531ee 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_badge-box.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_badge-box.scss @@ -33,5 +33,20 @@ margin: 6px 0px 0px 0px; width: fit-content; } + + @media screen and (max-width: 480px) { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + flex-direction: column; + + .ppcp-r-badge-box__title-text:not(:empty) + .ppcp-r-badge-box__title-image-badge { + margin: 0px; + img:first-of-type { + margin: 0px; + } + } + } } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss index d6d8cf4f3..70348532e 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_onboarding-header.scss @@ -31,5 +31,9 @@ @include font(14, 22, 400); margin: 0 20%; text-align: center; + + @media screen and (max-width: 480px) { + margin: 0px; + } } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_welcome-docs.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_welcome-docs.scss index 411d5a987..f6dce1407 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_welcome-docs.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_welcome-docs.scss @@ -20,8 +20,7 @@ justify-content: center; @media screen and (max-width: 480px) { - flex-wrap: wrap; - row-gap: 8px; + display: block; } } } diff --git a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss index 3399a1bc9..47af9c99a 100644 --- a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss +++ b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss @@ -78,6 +78,7 @@ border-right: 0; padding-right: 0; padding-bottom: 8px; + margin: 0px; } } } From 84060d1b2903cc4c692c544fc08d6ba032aa5d2e Mon Sep 17 00:00:00 2001 From: Himad M Date: Thu, 5 Dec 2024 09:33:24 -0400 Subject: [PATCH 32/93] New Settings UI: Set Optional Payment Methods default to null --- modules/ppcp-settings/resources/js/data/onboarding/reducer.js | 2 +- modules/ppcp-settings/src/Data/OnboardingProfile.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index 176d4875d..5b59b75e6 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -27,7 +27,7 @@ const defaultPersistent = { completed: false, step: 0, isCasualSeller: null, // null value will uncheck both options in the UI. - areOptionalPaymentMethodsEnabled: true, + areOptionalPaymentMethodsEnabled: null, products: [], }; diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index 03a0a7d1c..9381b58ee 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -67,7 +67,7 @@ protected function get_defaults() : array { 'completed' => false, 'step' => 0, 'is_casual_seller' => null, - 'are_optional_payment_methods_enabled' => true, + 'are_optional_payment_methods_enabled' => null, 'products' => array(), ); } From 2302f6ce18c06a45e36f9f5ee313392f4cbc4975 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 14:52:10 +0100 Subject: [PATCH 33/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20translatio?= =?UTF-8?q?ns=20to=20global=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index e9a2e30bc..e9a62c250 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -5,6 +5,26 @@ import { store as noticesStore } from '@wordpress/notices'; import { CommonHooks, OnboardingHooks } from '../data'; import { openPopup } from '../utils/window'; +const MESSAGES = { + CONNECTED: __( 'Connected to PayPal', 'woocommerce-paypal-payments' ), + POPUP_BLOCKED: __( + 'Popup blocked. Please allow popups for this site to connect to PayPal.', + 'woocommerce-paypal-payments' + ), + SANDBOX_ERROR: __( + 'Could not generate a Sandbox login link.', + 'woocommerce-paypal-payments' + ), + PRODUCTION_ERROR: __( + 'Could not generate a login link.', + 'woocommerce-paypal-payments' + ), + MANUAL_ERROR: __( + 'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.', + 'woocommerce-paypal-payments' + ), +}; + const useCommonConnectionLogic = () => { const { setCompleted } = OnboardingHooks.useSteps(); const { createSuccessNotice, createErrorNotice } = @@ -16,10 +36,8 @@ const useCommonConnectionLogic = () => { }; const handleServerSuccess = () => { - createSuccessNotice( - __( 'Connected to PayPal', 'woocommerce-paypal-payments' ) - ); - setCompleted( true ); + createSuccessNotice( MESSAGES.CONNECTED ); + return setCompleted( true ); }; return { handleServerError, handleServerSuccess, createErrorNotice }; @@ -34,13 +52,7 @@ export const useSandboxConnection = () => { const res = await connectToSandbox(); if ( ! res.success || ! res.data ) { - handleServerError( - res, - __( - 'Could not generate a Sandbox login link.', - 'woocommerce-paypal-payments' - ) - ); + handleServerError( res, MESSAGES.SANDBOX_ERROR ); return; } @@ -48,12 +60,7 @@ export const useSandboxConnection = () => { const popup = openPopup( connectionUrl ); if ( ! popup ) { - createErrorNotice( - __( - 'Popup blocked. Please allow popups for this site to connect to PayPal.', - 'woocommerce-paypal-payments' - ) - ); + createErrorNotice( MESSAGES.POPUP_BLOCKED ); } }; @@ -89,15 +96,9 @@ export const useManualConnection = () => { const res = await connectViaIdAndSecret(); if ( res.success ) { - handleServerSuccess(); + await handleServerSuccess(); } else { - handleServerError( - res, - __( - 'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.', - 'woocommerce-paypal-payments' - ) - ); + handleServerError( res, MESSAGES.MANUAL_ERROR ); } }; From 5a81d7378fddd871d6ce19fb940188732514f5fa Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:02:02 +0100 Subject: [PATCH 34/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Restructure=20intern?= =?UTF-8?q?al=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index e9a62c250..eed6f4c51 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -25,34 +25,34 @@ const MESSAGES = { ), }; -const useCommonConnectionLogic = () => { +const useConnectionBase = () => { const { setCompleted } = OnboardingHooks.useSteps(); const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); - const handleServerError = ( res, genericMessage ) => { - console.error( 'Connection error', res ); - createErrorNotice( res?.message ?? genericMessage ); - }; - - const handleServerSuccess = () => { - createSuccessNotice( MESSAGES.CONNECTED ); - return setCompleted( true ); + return { + handleError: ( res, genericMessage ) => { + console.error( 'Connection error', res ); + createErrorNotice( res?.message ?? genericMessage ); + }, + handleSuccess: async () => { + createSuccessNotice( MESSAGES.CONNECTED ); + return setCompleted( true ); + }, + createErrorNotice, }; - - return { handleServerError, handleServerSuccess, createErrorNotice }; }; export const useSandboxConnection = () => { const { connectToSandbox, isSandboxMode, setSandboxMode } = CommonHooks.useSandbox(); - const { handleServerError, createErrorNotice } = useCommonConnectionLogic(); + const { handleError, createErrorNotice } = useConnectionBase(); const handleSandboxConnect = async () => { const res = await connectToSandbox(); if ( ! res.success || ! res.data ) { - handleServerError( res, MESSAGES.SANDBOX_ERROR ); + handleError( res, MESSAGES.SANDBOX_ERROR ); return; } @@ -81,8 +81,8 @@ export const useManualConnection = () => { clientSecret, setClientSecret, } = CommonHooks.useManualConnection(); - const { handleServerError, handleServerSuccess, createErrorNotice } = - useCommonConnectionLogic(); + const { handleError, handleSuccess, createErrorNotice } = + useConnectionBase(); const handleConnectViaIdAndSecret = async ( { validation } = {} ) => { if ( 'function' === typeof validation ) { @@ -96,9 +96,9 @@ export const useManualConnection = () => { const res = await connectViaIdAndSecret(); if ( res.success ) { - await handleServerSuccess(); + await handleSuccess(); } else { - handleServerError( res, MESSAGES.MANUAL_ERROR ); + handleError( res, MESSAGES.MANUAL_ERROR ); } }; From 67822b3b11cc476a79f90d1bd00e2c6a3ee3a0e5 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:03:03 +0100 Subject: [PATCH 35/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Restructure=20Sandbo?= =?UTF-8?q?x=20connection=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index eed6f4c51..ce4ab441c 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -25,6 +25,30 @@ const MESSAGES = { ), }; +const handlePopupOpen = ( url, onError ) => { + const popup = openPopup( url ); + if ( ! popup ) { + onError( MESSAGES.POPUP_BLOCKED ); + return false; + } + return true; +}; + +const useConnectionAttempt = ( connectFn, errorMessage ) => { + const { handleError, createErrorNotice } = useConnectionBase(); + + return async ( ...args ) => { + const res = await connectFn( ...args ); + + if ( ! res.success || ! res.data ) { + handleError( res, errorMessage ); + return false; + } + + return handlePopupOpen( res.data, createErrorNotice ); + }; +}; + const useConnectionBase = () => { const { setCompleted } = OnboardingHooks.useSteps(); const { createSuccessNotice, createErrorNotice } = @@ -46,23 +70,10 @@ const useConnectionBase = () => { export const useSandboxConnection = () => { const { connectToSandbox, isSandboxMode, setSandboxMode } = CommonHooks.useSandbox(); - const { handleError, createErrorNotice } = useConnectionBase(); - - const handleSandboxConnect = async () => { - const res = await connectToSandbox(); - - if ( ! res.success || ! res.data ) { - handleError( res, MESSAGES.SANDBOX_ERROR ); - return; - } - - const connectionUrl = res.data; - const popup = openPopup( connectionUrl ); - - if ( ! popup ) { - createErrorNotice( MESSAGES.POPUP_BLOCKED ); - } - }; + const handleSandboxConnect = useConnectionAttempt( + connectToSandbox, + MESSAGES.SANDBOX_ERROR + ); return { handleSandboxConnect, From a8f12c63fa6deac89e87c2cc7e9f2d7d9b9ba0e5 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:04:36 +0100 Subject: [PATCH 36/93] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20re-organization=20?= =?UTF-8?q?of=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index ce4ab441c..ed2fd9f7b 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -34,21 +34,6 @@ const handlePopupOpen = ( url, onError ) => { return true; }; -const useConnectionAttempt = ( connectFn, errorMessage ) => { - const { handleError, createErrorNotice } = useConnectionBase(); - - return async ( ...args ) => { - const res = await connectFn( ...args ); - - if ( ! res.success || ! res.data ) { - handleError( res, errorMessage ); - return false; - } - - return handlePopupOpen( res.data, createErrorNotice ); - }; -}; - const useConnectionBase = () => { const { setCompleted } = OnboardingHooks.useSteps(); const { createSuccessNotice, createErrorNotice } = @@ -67,6 +52,21 @@ const useConnectionBase = () => { }; }; +const useConnectionAttempt = ( connectFn, errorMessage ) => { + const { handleError, createErrorNotice } = useConnectionBase(); + + return async ( ...args ) => { + const res = await connectFn( ...args ); + + if ( ! res.success || ! res.data ) { + handleError( res, errorMessage ); + return false; + } + + return handlePopupOpen( res.data, createErrorNotice ); + }; +}; + export const useSandboxConnection = () => { const { connectToSandbox, isSandboxMode, setSandboxMode } = CommonHooks.useSandbox(); @@ -83,6 +83,8 @@ export const useSandboxConnection = () => { }; export const useManualConnection = () => { + const { handleError, handleSuccess, createErrorNotice } = + useConnectionBase(); const { connectViaIdAndSecret, isManualConnectionMode, @@ -92,8 +94,6 @@ export const useManualConnection = () => { clientSecret, setClientSecret, } = CommonHooks.useManualConnection(); - const { handleError, handleSuccess, createErrorNotice } = - useConnectionBase(); const handleConnectViaIdAndSecret = async ( { validation } = {} ) => { if ( 'function' === typeof validation ) { @@ -104,6 +104,7 @@ export const useManualConnection = () => { return; } } + const res = await connectViaIdAndSecret(); if ( res.success ) { From 05c1978f0dd3086e09b5d50d43dcad7fe7099fe7 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:05:00 +0100 Subject: [PATCH 37/93] =?UTF-8?q?=E2=9C=A8=20Add=20new=20hook=20for=20prod?= =?UTF-8?q?uction=20ISU=20login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/onboarding/hooks.js | 13 ++++++++++++- .../resources/js/data/onboarding/selectors.js | 15 +++++++++++++++ .../resources/js/hooks/useHandleConnections.js | 11 +++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js index 5da85634f..96bae287c 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -34,8 +34,12 @@ const useHooks = () => { setProducts, } = useDispatch( STORE_NAME ); - // Read-only flags. + // Read-only flags and derived state. const flags = useSelect( ( select ) => select( STORE_NAME ).flags(), [] ); + const determineProducts = useSelect( + ( select ) => select( STORE_NAME ).determineProducts(), + [] + ); // Transient accessors. const isReady = useTransient( 'isReady' ); @@ -80,6 +84,7 @@ const useHooks = () => { ); return savePersistent( setProducts, validProducts ); }, + determineProducts, }; }; @@ -123,3 +128,9 @@ export const useNavigationState = () => { business, }; }; + +export const useDetermineProducts = () => { + const { determineProducts } = useHooks(); + + return determineProducts; +}; diff --git a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js index d4d57ef4d..63296d8a4 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js @@ -23,3 +23,18 @@ export const transientData = ( state ) => { export const flags = ( state ) => { return getState( state ).flags || EMPTY_OBJ; }; + +/** + * Returns the products that we use for the production login link in the last onboarding step. + * + * This selector does not return state-values, but uses the state to derive the products-array + * that should be returned. + * + * @param {{}} state + * @return {string[]} The ISU products, based on choices made in the onboarding wizard. + */ +export const determineProducts = ( state ) => { + const derivedProducts = []; + + return derivedProducts; +}; diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index ed2fd9f7b..2c3cfcea7 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -82,6 +82,17 @@ export const useSandboxConnection = () => { }; }; +export const useProductionConnection = () => { + const { connectToProduction } = CommonHooks.useProduction(); + const products = OnboardingHooks.useDetermineProducts(); + const handleProductionConnect = useConnectionAttempt( + () => connectToProduction( products ), + MESSAGES.PRODUCTION_ERROR + ); + + return { handleProductionConnect }; +}; + export const useManualConnection = () => { const { handleError, handleSuccess, createErrorNotice } = useConnectionBase(); From 0e30ecde82182b331ca7df4cf2ccafa01e3fd241 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:06:34 +0100 Subject: [PATCH 38/93] =?UTF-8?q?=E2=9C=A8=20Add=20production=20login=20ho?= =?UTF-8?q?ok=20to=20last=20wizard=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/Components/ConnectionButton.js | 8 ++++++-- modules/ppcp-settings/resources/js/data/common/hooks.js | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js index 8fb4b235c..5d1bae47e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -3,7 +3,10 @@ import { Button } from '@wordpress/components'; import classNames from 'classnames'; import { openSignup } from '../../../ReusableComponents/Icons'; -import { useSandboxConnection } from '../../../../hooks/useHandleConnections'; +import { + useProductionConnection, + useSandboxConnection, +} from '../../../../hooks/useHandleConnections'; const ConnectionButton = ( { title, @@ -12,6 +15,7 @@ const ConnectionButton = ( { showIcon = true, } ) => { const { handleSandboxConnect } = useSandboxConnection(); + const { handleProductionConnect } = useProductionConnection(); const className = classNames( 'ppcp-r-connection-button', { 'sandbox-mode': isSandbox, 'live-mode': ! isSandbox, @@ -21,7 +25,7 @@ const ConnectionButton = ( { if ( isSandbox ) { await handleSandboxConnect(); } else { - console.warn( 'Live connection not implemented yet' ); + await handleProductionConnect(); } }; diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index 3f9185102..c1fa859d9 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -32,6 +32,7 @@ const useHooks = () => { setClientId, setClientSecret, connectToSandbox, + connectToProduction, connectViaIdAndSecret, } = useDispatch( STORE_NAME ); @@ -73,6 +74,7 @@ const useHooks = () => { return savePersistent( setClientSecret, value ); }, connectToSandbox, + connectToProduction, connectViaIdAndSecret, wooSettings, }; @@ -94,6 +96,12 @@ export const useSandbox = () => { return { isSandboxMode, setSandboxMode, connectToSandbox }; }; +export const useProduction = () => { + const { connectToProduction } = useHooks(); + + return { connectToProduction }; +}; + export const useManualConnection = () => { const { isManualConnectionMode, From 1a36144095f85dd653b387b450a3bca5139adb68 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 15:06:59 +0100 Subject: [PATCH 39/93] =?UTF-8?q?=F0=9F=91=94=20Start=20to=20customize=20t?= =?UTF-8?q?he=20production=20ISU=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Endpoint/PartnerReferrals.php | 2 ++ .../resources/js/data/onboarding/selectors.js | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/modules/ppcp-api-client/src/Endpoint/PartnerReferrals.php b/modules/ppcp-api-client/src/Endpoint/PartnerReferrals.php index c7f5ec131..1d100cdda 100644 --- a/modules/ppcp-api-client/src/Endpoint/PartnerReferrals.php +++ b/modules/ppcp-api-client/src/Endpoint/PartnerReferrals.php @@ -16,6 +16,8 @@ /** * Class PartnerReferrals + * + * @see https://developer.paypal.com/docs/api/partner-referrals/v2/ */ class PartnerReferrals { diff --git a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js index 63296d8a4..2e0953437 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js @@ -36,5 +36,37 @@ export const flags = ( state ) => { export const determineProducts = ( state ) => { const derivedProducts = []; + const { isCasualSeller, areOptionalPaymentMethodsEnabled } = + persistentData( state ); + const { canUseVaulting, canUseCardPayments } = flags( state ); + + if ( ! canUseCardPayments || ! areOptionalPaymentMethodsEnabled ) { + /** + * Branch 1: Credit Card Payments not available. + * The store uses the Express-checkout product. + */ + derivedProducts.push( 'EXPRESS_CHECKOUT' ); + } else if ( isCasualSeller ) { + /** + * Branch 2: Merchant has no business. + * The store uses the Express-checkout product. + */ + derivedProducts.push( 'EXPRESS_CHECKOUT' ); + + // TODO: Add the "BCDC" product/feature + // Requirement: "EXPRESS_CHECKOUT with BCDC" + } else { + /** + * Branch 3: Merchant is business, and can use CC payments. + * The store uses the advanced PPCP product. + */ + derivedProducts.push( 'PPCP' ); + } + + if ( canUseVaulting ) { + // TODO: Add the "Vaulting" product/feature + // Requirement: "... with Vault" + } + return derivedProducts; }; From 137d40489cc61f82a7dd8d13e2a9b5c3c86ac527 Mon Sep 17 00:00:00 2001 From: Himad M Date: Thu, 5 Dec 2024 10:33:34 -0400 Subject: [PATCH 40/93] New Settings UI: Conditionally show subscriptions product option --- .../Screens/Onboarding/StepProducts.js | 49 ++++++++++--------- .../resources/js/data/onboarding/hooks.js | 5 ++ .../resources/js/data/onboarding/reducer.js | 1 + modules/ppcp-settings/services.php | 4 +- .../src/Data/OnboardingProfile.php | 4 +- .../src/Endpoint/OnboardingRestEndpoint.php | 3 ++ 6 files changed, 41 insertions(+), 25 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js index cbd642327..ee99f4acf 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js @@ -9,6 +9,7 @@ const PRODUCTS_CHECKBOX_GROUP_NAME = 'products'; const StepProducts = () => { const { products, setProducts } = OnboardingHooks.useProducts(); + const { canUseSubscriptions } = OnboardingHooks.useFlags(); return (
diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js index 5da85634f..b0f41d450 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -123,3 +123,8 @@ export const useNavigationState = () => { business, }; }; + +export const useFlags = () => { + const { flags } = useHooks(); + return flags; +}; diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index 5b59b75e6..4ceb53f20 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -20,6 +20,7 @@ const defaultTransient = { canUseCasualSelling: false, canUseVaulting: false, canUseCardPayments: false, + canUseSubscriptions: false, }, }; diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index 2c646f054..31880dca0 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -37,6 +37,7 @@ $can_use_casual_selling = $container->get( 'settings.casual-selling.eligible' ); $can_use_vaulting = $container->has( 'save-payment-methods.eligible' ) && $container->get( 'save-payment-methods.eligible' ); $can_use_card_payments = $container->has( 'card-fields.eligible' ) && $container->get( 'card-fields.eligible' ); + $can_use_subscriptions = $container->has( 'wc-subscriptions.helper' ) && $container->get( 'wc-subscriptions.helper' )->plugin_is_active(); // Card payments are disabled for this plugin when WooPayments is active. // TODO: Move this condition to the card-fields.eligible service? @@ -47,7 +48,8 @@ return new OnboardingProfile( $can_use_casual_selling, $can_use_vaulting, - $can_use_card_payments + $can_use_card_payments, + $can_use_subscriptions ); }, 'settings.data.general' => static function ( ContainerInterface $container ) : GeneralSettings { diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index 9381b58ee..633f13269 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -48,13 +48,15 @@ class OnboardingProfile extends AbstractDataModel { public function __construct( bool $can_use_casual_selling = false, bool $can_use_vaulting = false, - bool $can_use_card_payments = false + bool $can_use_card_payments = false, + bool $can_use_subscriptions = false ) { parent::__construct(); $this->flags['can_use_casual_selling'] = $can_use_casual_selling; $this->flags['can_use_vaulting'] = $can_use_vaulting; $this->flags['can_use_card_payments'] = $can_use_card_payments; + $this->flags['can_use_subscriptions'] = $can_use_subscriptions; } /** diff --git a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php index 02e7c80cd..d4273228f 100644 --- a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php @@ -77,6 +77,9 @@ class OnboardingRestEndpoint extends RestEndpoint { 'can_use_card_payments' => array( 'js_name' => 'canUseCardPayments', ), + 'can_use_subscriptions' => array( + 'js_name' => 'canUseSubscriptions', + ), ); /** From e502f78376d8527e3ad427be4aac00f20486535b Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 16:09:28 +0100 Subject: [PATCH 41/93] =?UTF-8?q?=E2=9C=A8=20Add=20automatic=20popup=20com?= =?UTF-8?q?pletion=20tracking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index 2c3cfcea7..6573f2478 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -25,13 +25,32 @@ const MESSAGES = { ), }; -const handlePopupOpen = ( url, onError ) => { - const popup = openPopup( url ); - if ( ! popup ) { - onError( MESSAGES.POPUP_BLOCKED ); - return false; - } - return true; +const handlePopupWithCompletion = ( url, onError ) => { + return new Promise( ( resolve ) => { + const popup = openPopup( url ); + + if ( ! popup ) { + onError( MESSAGES.POPUP_BLOCKED ); + resolve( false ); + return; + } + + // Check popup state every 500ms + const checkPopup = setInterval( () => { + if ( popup.closed ) { + clearInterval( checkPopup ); + resolve( true ); + } + }, 500 ); + + return () => { + clearInterval( checkPopup ); + + if ( popup && ! popup.closed ) { + popup.close(); + } + }; + } ); }; const useConnectionBase = () => { @@ -46,6 +65,8 @@ const useConnectionBase = () => { }, handleSuccess: async () => { createSuccessNotice( MESSAGES.CONNECTED ); + + // TODO: Contact the plugin to confirm onboarding is completed. return setCompleted( true ); }, createErrorNotice, @@ -53,7 +74,8 @@ const useConnectionBase = () => { }; const useConnectionAttempt = ( connectFn, errorMessage ) => { - const { handleError, createErrorNotice } = useConnectionBase(); + const { handleError, createErrorNotice, handleSuccess } = + useConnectionBase(); return async ( ...args ) => { const res = await connectFn( ...args ); @@ -63,7 +85,16 @@ const useConnectionAttempt = ( connectFn, errorMessage ) => { return false; } - return handlePopupOpen( res.data, createErrorNotice ); + const popupClosed = await handlePopupWithCompletion( + res.data, + createErrorNotice + ); + + if ( popupClosed ) { + await handleSuccess(); + } + + return popupClosed; }; }; From 51db2de840b02d80c0f7c713cb09607c47083ed0 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 16:22:26 +0100 Subject: [PATCH 42/93] =?UTF-8?q?=E2=9C=A8=20Add=20a=20default=20=E2=80=9C?= =?UTF-8?q?reset=E2=80=9D=20action=20to=20every=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/action-types.js | 1 + .../resources/js/data/common/actions.js | 7 +++++++ .../resources/js/data/common/reducer.js | 13 +++++++++++++ .../resources/js/data/onboarding/reducer.js | 14 ++++++++++++-- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js index 3b1e1fcf3..0cfe2e758 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -10,6 +10,7 @@ export default { // Persistent data. SET_PERSISTENT: 'COMMON:SET_PERSISTENT', + RESET: 'COMMON:RESET', HYDRATE: 'COMMON:HYDRATE', // Controls - always start with "DO_". diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 6b1a03b4e..6aea05024 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -18,6 +18,13 @@ import { STORE_NAME } from './constants'; * @property {Object?} payload - Optional payload for the action. */ +/** + * Special. Resets all values in the onboarding store to initial defaults. + * + * @return {Action} The action. + */ +export const reset = () => ( { type: ACTION_TYPES.RESET } ); + /** * Persistent. Set the full onboarding details, usually during app initialization. * diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 771dfa8f5..814f4d6b3 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -44,6 +44,19 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { [ ACTION_TYPES.SET_PERSISTENT ]: ( state, action ) => setPersistent( state, action ), + [ ACTION_TYPES.RESET ]: ( state ) => { + const cleanState = setTransient( + setPersistent( state, defaultPersistent ), + defaultTransient + ); + + // Keep "read-only" details and initialization flags. + cleanState.wooSettings = { ...state.wooSettings }; + cleanState.isReady = true; + + return cleanState; + }, + [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { const newState = setPersistent( state, payload.data ); diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index 176d4875d..65d82d2a5 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -45,8 +45,18 @@ const onboardingReducer = createReducer( defaultTransient, defaultPersistent, { [ ACTION_TYPES.SET_PERSISTENT ]: ( state, payload ) => setPersistent( state, payload ), - [ ACTION_TYPES.RESET ]: ( state ) => - setPersistent( state, defaultPersistent ), + [ ACTION_TYPES.RESET ]: ( state ) => { + const cleanState = setTransient( + setPersistent( state, defaultPersistent ), + defaultTransient + ); + + // Keep "read-only" details and initialization flags. + cleanState.flags = { ...state.flags }; + cleanState.isReady = true; + + return cleanState; + }, [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { const newState = setPersistent( state, payload.data ); From 405c397331af31a4dc3a490edbc9802f881e626d Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 16:23:09 +0100 Subject: [PATCH 43/93] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Corre?= =?UTF-8?q?ct=20debug=20method=20to=20reset=20all=20stores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/resources/js/data/debug.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/debug.js b/modules/ppcp-settings/resources/js/data/debug.js index b292d1920..6380c6d6a 100644 --- a/modules/ppcp-settings/resources/js/data/debug.js +++ b/modules/ppcp-settings/resources/js/data/debug.js @@ -1,4 +1,4 @@ -import { OnboardingStoreName } from './index'; +import { OnboardingStoreName, CommonStoreName } from './index'; export const addDebugTools = ( context, modules ) => { if ( ! context || ! context?.debug ) { @@ -33,9 +33,14 @@ export const addDebugTools = ( context, modules ) => { }; context.resetStore = () => { - const onboarding = wp.data.dispatch( OnboardingStoreName ); - onboarding.reset(); - onboarding.persist(); + const stores = [ OnboardingStoreName, CommonStoreName ]; + + stores.forEach( ( storeName ) => { + const store = wp.data.dispatch( storeName ); + + store.reset(); + store.persist(); + } ); }; context.startOnboarding = () => { From 9786a18eb0e2322e67510bcfcc405913af0e4bab Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 18:55:56 +0100 Subject: [PATCH 44/93] =?UTF-8?q?=E2=9C=A8=20Replace=20isBusy=20with=20new?= =?UTF-8?q?=20activity-state=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/action-types.js | 4 ++ .../resources/js/data/common/actions.js | 37 +++++++++++++------ .../resources/js/data/common/hooks.js | 30 +++++++++++++-- .../resources/js/data/common/reducer.js | 17 ++++++++- .../resources/js/data/common/selectors.js | 5 +++ 5 files changed, 77 insertions(+), 16 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js index 0cfe2e758..ac2c6db37 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -13,6 +13,10 @@ export default { RESET: 'COMMON:RESET', HYDRATE: 'COMMON:HYDRATE', + // Activity management (advanced solution that replaces the isBusy state). + START_ACTIVITY: 'COMMON:START_ACTIVITY', + STOP_ACTIVITY: 'COMMON:STOP_ACTIVITY', + // Controls - always start with "DO_". DO_PERSIST_DATA: 'COMMON:DO_PERSIST_DATA', DO_MANUAL_CONNECTION: 'COMMON:DO_MANUAL_CONNECTION', diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 6aea05024..c6906546a 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -59,14 +59,35 @@ export const setIsSaving = ( isSaving ) => ( { } ); /** - * Transient. Changes the "manual connection is busy" flag. + * Transient (Activity): Marks the start of an async activity + * Think of it as "setIsBusy(true)" * - * @param {boolean} isBusy + * @param {string} id Internal ID/key of the action, used to stop it again. + * @param {?string} description Optional, description for logging/debugging + * @return {?Action} The action. + */ +export const startActivity = ( id, description = null ) => { + if ( ! id || 'string' !== typeof id ) { + console.warn( 'Activity ID must be a non-empty string' ); + return null; + } + + return { + type: ACTION_TYPES.START_ACTIVITY, + payload: { id, description }, + }; +}; + +/** + * Transient (Activity): Marks the end of an async activity. + * Think of it as "setIsBusy(false)" + * + * @param {string} id Internal ID/key of the action, used to stop it again. * @return {Action} The action. */ -export const setIsBusy = ( isBusy ) => ( { - type: ACTION_TYPES.SET_TRANSIENT, - payload: { isBusy }, +export const stopActivity = ( id ) => ( { + type: ACTION_TYPES.STOP_ACTIVITY, + payload: { id }, } ); /** @@ -130,10 +151,8 @@ export const persist = function* () { * @return {Action} The action. */ export const connectToSandbox = function* () { - yield setIsBusy( true ); const result = yield { type: ACTION_TYPES.DO_SANDBOX_LOGIN }; - yield setIsBusy( false ); return result; }; @@ -145,13 +164,11 @@ export const connectToSandbox = function* () { * @return {Action} The action. */ export const connectToProduction = function* ( products = [] ) { - yield setIsBusy( true ); const result = yield { type: ACTION_TYPES.DO_PRODUCTION_LOGIN, products, }; - yield setIsBusy( false ); return result; }; @@ -165,7 +182,6 @@ export const connectViaIdAndSecret = function* () { const { clientId, clientSecret, useSandbox } = yield select( STORE_NAME ).persistentData(); - yield setIsBusy( true ); const result = yield { type: ACTION_TYPES.DO_MANUAL_CONNECTION, @@ -173,7 +189,6 @@ export const connectViaIdAndSecret = function* () { clientSecret, useSandbox, }; - yield setIsBusy( false ); return result; }; diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index c1fa859d9..e4442e50f 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -81,12 +81,34 @@ const useHooks = () => { }; export const useBusyState = () => { - const { setIsBusy } = useDispatch( STORE_NAME ); - const isBusy = useTransient( 'isBusy' ); + const { startActivity, stopActivity } = useDispatch( STORE_NAME ); + + // Resolved value (object), contains a list of all running actions. + const activities = useSelect( + ( select ) => select( STORE_NAME ).getActivityList(), + [] + ); + + // Derive isBusy state from activities + const isBusy = Object.keys( activities ).length > 0; + + // HOC that starts and stops an activity while the callback is executed. + const withActivity = useCallback( + async ( id, description, asyncFn ) => { + startActivity( id, description ); + try { + return await asyncFn(); + } finally { + stopActivity( id ); + } + }, + [ startActivity, stopActivity ] + ); return { - isBusy, - setIsBusy: useCallback( ( busy ) => setIsBusy( busy ), [ setIsBusy ] ), + withActivity, // HOC + isBusy, // Boolean. + activities, // Object. }; }; diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 814f4d6b3..63c231f85 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -14,7 +14,7 @@ import ACTION_TYPES from './action-types'; const defaultTransient = { isReady: false, - isBusy: false, + activities: new Map(), // Read only values, provided by the server via hydrate. wooSettings: { @@ -57,6 +57,21 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { return cleanState; }, + [ ACTION_TYPES.START_ACTIVITY ]: ( state, payload ) => { + return setTransient( state, { + activities: new Map( state.activities ).set( + payload.id, + payload.description + ), + } ); + }, + + [ ACTION_TYPES.STOP_ACTIVITY ]: ( state, payload ) => { + const newActivities = new Map( state.activities ); + newActivities.delete( payload.id ); + return setTransient( state, { activities: newActivities } ); + }, + [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { const newState = setPersistent( state, payload.data ); diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js index 7f0b3ee20..17e422b7a 100644 --- a/modules/ppcp-settings/resources/js/data/common/selectors.js +++ b/modules/ppcp-settings/resources/js/data/common/selectors.js @@ -20,6 +20,11 @@ export const transientData = ( state ) => { return transientState || EMPTY_OBJ; }; +export const getActivityList = ( state ) => { + const { activities = new Map() } = state; + return Object.fromEntries( activities ); +}; + export const wooSettings = ( state ) => { return getState( state ).wooSettings || EMPTY_OBJ; }; From 64ea8ec011de032589f42cc5c16f6967fb0385fa Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 18:56:23 +0100 Subject: [PATCH 45/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20some=20ac?= =?UTF-8?q?tion=20generators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/actions.js | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index c6906546a..abed4f2d3 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -146,31 +146,22 @@ export const persist = function* () { }; /** - * Side effect. Initiates the sandbox login ISU. + * Side effect. Fetches the ISU-login URL for a sandbox account. * * @return {Action} The action. */ export const connectToSandbox = function* () { - - const result = yield { type: ACTION_TYPES.DO_SANDBOX_LOGIN }; - - return result; + return yield { type: ACTION_TYPES.DO_SANDBOX_LOGIN }; }; /** - * Side effect. Initiates the production login ISU. + * Side effect. Fetches the ISU-login URL for a production account. * * @param {string[]} products Which products/features to display in the ISU popup. * @return {Action} The action. */ export const connectToProduction = function* ( products = [] ) { - - const result = yield { - type: ACTION_TYPES.DO_PRODUCTION_LOGIN, - products, - }; - - return result; + return yield { type: ACTION_TYPES.DO_PRODUCTION_LOGIN, products }; }; /** @@ -182,13 +173,10 @@ export const connectViaIdAndSecret = function* () { const { clientId, clientSecret, useSandbox } = yield select( STORE_NAME ).persistentData(); - - const result = yield { + return yield { type: ACTION_TYPES.DO_MANUAL_CONNECTION, clientId, clientSecret, useSandbox, }; - - return result; }; From 5b26d7c5ca165ef34c96989f8f8158600d2a4f63 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 18:56:43 +0100 Subject: [PATCH 46/93] =?UTF-8?q?=E2=9C=A8=20Use=20new=20actitiy-state=20f?= =?UTF-8?q?or=20login=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 67 ++++++++++++++----- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index 6573f2478..d242b1de9 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -25,6 +25,12 @@ const MESSAGES = { ), }; +const ACTIVITIES = { + CONNECT_SANDBOX: 'ISU_LOGIN_SANDBOX', + CONNECT_PRODUCTION: 'ISU_LOGIN_PRODUCTION', + CONNECT_MANUAL: 'MANUAL_LOGIN', +}; + const handlePopupWithCompletion = ( url, onError ) => { return new Promise( ( resolve ) => { const popup = openPopup( url ); @@ -101,11 +107,20 @@ const useConnectionAttempt = ( connectFn, errorMessage ) => { export const useSandboxConnection = () => { const { connectToSandbox, isSandboxMode, setSandboxMode } = CommonHooks.useSandbox(); - const handleSandboxConnect = useConnectionAttempt( + const { withActivity } = CommonHooks.useBusyState(); + const connectionAttempt = useConnectionAttempt( connectToSandbox, MESSAGES.SANDBOX_ERROR ); + const handleSandboxConnect = async () => { + return withActivity( + ACTIVITIES.CONNECT_SANDBOX, + 'Connecting to sandbox account', + connectionAttempt + ); + }; + return { handleSandboxConnect, isSandboxMode, @@ -115,18 +130,28 @@ export const useSandboxConnection = () => { export const useProductionConnection = () => { const { connectToProduction } = CommonHooks.useProduction(); + const { withActivity } = CommonHooks.useBusyState(); const products = OnboardingHooks.useDetermineProducts(); - const handleProductionConnect = useConnectionAttempt( + const connectionAttempt = useConnectionAttempt( () => connectToProduction( products ), MESSAGES.PRODUCTION_ERROR ); + const handleProductionConnect = async () => { + return withActivity( + ACTIVITIES.CONNECT_PRODUCTION, + 'Connecting to production account', + connectionAttempt + ); + }; + return { handleProductionConnect }; }; export const useManualConnection = () => { const { handleError, handleSuccess, createErrorNotice } = useConnectionBase(); + const { withActivity } = CommonHooks.useBusyState(); const { connectViaIdAndSecret, isManualConnectionMode, @@ -138,22 +163,30 @@ export const useManualConnection = () => { } = CommonHooks.useManualConnection(); const handleConnectViaIdAndSecret = async ( { validation } = {} ) => { - if ( 'function' === typeof validation ) { - try { - validation(); - } catch ( exception ) { - createErrorNotice( exception.message ); - return; + return withActivity( + ACTIVITIES.CONNECT_MANUAL, + 'Connecting manually via Client ID and Secret', + async () => { + if ( 'function' === typeof validation ) { + try { + validation(); + } catch ( exception ) { + createErrorNotice( exception.message ); + return; + } + } + + const res = await connectViaIdAndSecret(); + + if ( res.success ) { + await handleSuccess(); + } else { + handleError( res, MESSAGES.MANUAL_ERROR ); + } + + return res.success; } - } - - const res = await connectViaIdAndSecret(); - - if ( res.success ) { - await handleSuccess(); - } else { - handleError( res, MESSAGES.MANUAL_ERROR ); - } + ); }; return { From 3174bc158fe86852dcc92ab44cd8ecc36b09406b Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 14:37:21 +0100 Subject: [PATCH 47/93] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Minor=20performance?= =?UTF-8?q?=20tweaks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index cc6f49ae2..bb2d58209 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -1,6 +1,12 @@ import { __, sprintf } from '@wordpress/i18n'; import { Button, TextControl } from '@wordpress/components'; -import { useRef, useState, useEffect } from '@wordpress/element'; +import { + useRef, + useState, + useEffect, + useMemo, + useCallback, +} from '@wordpress/element'; import classNames from 'classnames'; @@ -33,8 +39,6 @@ const FORM_ERRORS = { const AdvancedOptionsForm = () => { const [ clientValid, setClientValid ] = useState( false ); const [ secretValid, setSecretValid ] = useState( false ); - const [ clientIdLabel, setClientIdLabel ] = useState( '' ); - const [ secretKeyLabel, setSecretKeyLabel ] = useState( '' ); const { isBusy } = CommonHooks.useBusyState(); const { isSandboxMode, setSandboxMode } = useSandboxConnection(); @@ -51,7 +55,7 @@ const AdvancedOptionsForm = () => { const refClientId = useRef( null ); const refClientSecret = useRef( null ); - const validateManualConnectionForm = () => { + const validateManualConnectionForm = useCallback( () => { const checks = [ { ref: refClientId, @@ -78,30 +82,36 @@ const AdvancedOptionsForm = () => { ref?.current?.focus(); throw new Error( errorMessage ); } - }; - - const handleManualConnect = () => - handleConnectViaIdAndSecret( { - validation: validateManualConnectionForm, - } ); + }, [ clientId, clientSecret, clientValid, secretValid ] ); + + const handleManualConnect = useCallback( + () => + handleConnectViaIdAndSecret( { + validation: validateManualConnectionForm, + } ), + [ validateManualConnectionForm ] + ); useEffect( () => { setClientValid( ! clientId || /^A[\w-]{79}$/.test( clientId ) ); setSecretValid( clientSecret && clientSecret.length > 0 ); }, [ clientId, clientSecret ] ); - useEffect( () => { - setClientIdLabel( + const clientIdLabel = useMemo( + () => isSandboxMode ? __( 'Sandbox Client ID', 'woocommerce-paypal-payments' ) - : __( 'Live Client ID', 'woocommerce-paypal-payments' ) - ); - setSecretKeyLabel( + : __( 'Live Client ID', 'woocommerce-paypal-payments' ), + [ isSandboxMode ] + ); + + const secretKeyLabel = useMemo( + () => isSandboxMode ? __( 'Sandbox Secret Key', 'woocommerce-paypal-payments' ) - : __( 'Live Secret Key', 'woocommerce-paypal-payments' ) - ); - }, [ isSandboxMode ] ); + : __( 'Live Secret Key', 'woocommerce-paypal-payments' ), + [ isSandboxMode ] + ); const advancedUsersDescription = sprintf( // translators: %s: Link to PayPal REST application guide From 4f3c4e6f3df17e35e07376f285b13666a329c27c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 14:37:48 +0100 Subject: [PATCH 48/93] =?UTF-8?q?=E2=9C=A8=20Disable=20the=20Connect-butto?= =?UTF-8?q?n=20during=20login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/Components/ConnectionButton.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js index 5d1bae47e..0a0ac5bfb 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -2,6 +2,7 @@ import { Button } from '@wordpress/components'; import classNames from 'classnames'; +import { CommonHooks } from '../../../../data'; import { openSignup } from '../../../ReusableComponents/Icons'; import { useProductionConnection, @@ -14,11 +15,13 @@ const ConnectionButton = ( { variant = 'primary', showIcon = true, } ) => { + const { isBusy } = CommonHooks.useBusyState(); const { handleSandboxConnect } = useSandboxConnection(); const { handleProductionConnect } = useProductionConnection(); const className = classNames( 'ppcp-r-connection-button', { 'sandbox-mode': isSandbox, 'live-mode': ! isSandbox, + 'ppcp--is-loading': isBusy, } ); const handleConnectClick = async () => { @@ -35,6 +38,7 @@ const ConnectionButton = ( { variant={ variant } icon={ showIcon ? openSignup : null } onClick={ handleConnectClick } + disabled={ isBusy } > { title } From 4e9d588058715894a180bf45d8c790729d6cab27 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 15:49:37 +0100 Subject: [PATCH 49/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rearrange=20code,=20?= =?UTF-8?q?minor=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/hooks.js | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index e4442e50f..de56976e5 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -80,38 +80,6 @@ const useHooks = () => { }; }; -export const useBusyState = () => { - const { startActivity, stopActivity } = useDispatch( STORE_NAME ); - - // Resolved value (object), contains a list of all running actions. - const activities = useSelect( - ( select ) => select( STORE_NAME ).getActivityList(), - [] - ); - - // Derive isBusy state from activities - const isBusy = Object.keys( activities ).length > 0; - - // HOC that starts and stops an activity while the callback is executed. - const withActivity = useCallback( - async ( id, description, asyncFn ) => { - startActivity( id, description ); - try { - return await asyncFn(); - } finally { - stopActivity( id ); - } - }, - [ startActivity, stopActivity ] - ); - - return { - withActivity, // HOC - isBusy, // Boolean. - activities, // Object. - }; -}; - export const useSandbox = () => { const { isSandboxMode, setSandboxMode, connectToSandbox } = useHooks(); @@ -148,5 +116,40 @@ export const useManualConnection = () => { export const useWooSettings = () => { const { wooSettings } = useHooks(); + return wooSettings; }; + +// -- Not using the `useHooks()` data provider -- + +export const useBusyState = () => { + const { startActivity, stopActivity } = useDispatch( STORE_NAME ); + + // Resolved value (object), contains a list of all running actions. + const activities = useSelect( + ( select ) => select( STORE_NAME ).getActivityList(), + [] + ); + + // Derive isBusy state from activities + const isBusy = Object.keys( activities ).length > 0; + + // HOC that starts and stops an activity while the callback is executed. + const withActivity = useCallback( + async ( id, description, asyncFn ) => { + startActivity( id, description ); + try { + return await asyncFn(); + } finally { + stopActivity( id ); + } + }, + [ startActivity, stopActivity ] + ); + + return { + withActivity, // HOC + isBusy, // Boolean. + activities, // Object. + }; +}; From 0502c25ddf684a1d56465838da355c376a0cc6a6 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 15:50:58 +0100 Subject: [PATCH 50/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20callback=20?= =?UTF-8?q?methods,=20minor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/hooks/useHandleConnections.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index d242b1de9..10d164cea 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -65,11 +65,11 @@ const useConnectionBase = () => { useDispatch( noticesStore ); return { - handleError: ( res, genericMessage ) => { + handleFailed: ( res, genericMessage ) => { console.error( 'Connection error', res ); createErrorNotice( res?.message ?? genericMessage ); }, - handleSuccess: async () => { + handleCompleted: async () => { createSuccessNotice( MESSAGES.CONNECTED ); // TODO: Contact the plugin to confirm onboarding is completed. @@ -80,14 +80,14 @@ const useConnectionBase = () => { }; const useConnectionAttempt = ( connectFn, errorMessage ) => { - const { handleError, createErrorNotice, handleSuccess } = + const { handleFailed, createErrorNotice, handleCompleted } = useConnectionBase(); return async ( ...args ) => { const res = await connectFn( ...args ); if ( ! res.success || ! res.data ) { - handleError( res, errorMessage ); + handleFailed( res, errorMessage ); return false; } @@ -97,7 +97,7 @@ const useConnectionAttempt = ( connectFn, errorMessage ) => { ); if ( popupClosed ) { - await handleSuccess(); + await handleCompleted(); } return popupClosed; @@ -149,7 +149,7 @@ export const useProductionConnection = () => { }; export const useManualConnection = () => { - const { handleError, handleSuccess, createErrorNotice } = + const { handleFailed, handleCompleted, createErrorNotice } = useConnectionBase(); const { withActivity } = CommonHooks.useBusyState(); const { @@ -179,9 +179,9 @@ export const useManualConnection = () => { const res = await connectViaIdAndSecret(); if ( res.success ) { - await handleSuccess(); + await handleCompleted(); } else { - handleError( res, MESSAGES.MANUAL_ERROR ); + handleFailed( res, MESSAGES.MANUAL_ERROR ); } return res.success; From a76b3471bc4896d69b34789192352dbc9adf114e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:04:54 +0100 Subject: [PATCH 51/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20REST=20contro?= =?UTF-8?q?ller=20more=20reusable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Endpoint/CommonRestEndpoint.php | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 721c07e11..62ee44ff7 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -123,17 +123,9 @@ public function get_details() : WP_REST_Response { $this->field_map ); - $js_woo_settings = $this->sanitize_for_javascript( - $this->settings->get_woo_settings(), - $this->woo_settings_map - ); + $extra_data = $this->add_woo_settings( array() ); - return $this->return_success( - $js_data, - array( - 'wooSettings' => $js_woo_settings, - ) - ); + return $this->return_success( $js_data, $extra_data ); } /** @@ -154,4 +146,21 @@ public function update_details( WP_REST_Request $request ) : WP_REST_Response { return $this->get_details(); } + + /** + * Appends the "wooSettings" attribute to the extra_data collection to + * provide WooCommerce store details, like the store country and currency. + * + * @param array $extra_data Initial extra_data collection. + * + * @return array Updated extra_data collection. + */ + protected function add_woo_settings( array $extra_data ) : array { + $extra_data['wooSettings'] = $this->sanitize_for_javascript( + $this->settings->get_woo_settings(), + $this->woo_settings_map + ); + + return $extra_data; + } } From 04629051234e457946bc4d492706ab12333f7f30 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:06:27 +0100 Subject: [PATCH 52/93] =?UTF-8?q?=E2=9C=A8=20Add=20merchant-connection=20d?= =?UTF-8?q?ata=20to=20CommonSettings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-settings/src/Data/CommonSettings.php | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/modules/ppcp-settings/src/Data/CommonSettings.php b/modules/ppcp-settings/src/Data/CommonSettings.php index b377b66aa..1894255ff 100644 --- a/modules/ppcp-settings/src/Data/CommonSettings.php +++ b/modules/ppcp-settings/src/Data/CommonSettings.php @@ -59,6 +59,12 @@ protected function get_defaults() : array { 'use_manual_connection' => false, 'client_id' => '', 'client_secret' => '', + + // Details about connected merchant account. + 'merchant_connected' => false, + 'sandbox_merchant' => false, + 'merchant_id' => '', + 'merchant_email' => '', ); } @@ -144,4 +150,58 @@ public function set_client_secret( string $client_secret ) : void { public function get_woo_settings() : array { return $this->woo_settings; } + + /** + * Setter to update details of the connected merchant account. + * + * Those details cannot be changed individually. + * + * @param bool $is_sandbox Whether the details are for a sandbox account. + * @param string $merchant_id The merchant ID. + * @param string $merchant_email The merchant's email. + * + * @return void + */ + public function set_merchant_data( bool $is_sandbox, string $merchant_id, string $merchant_email ) : void { + $this->data['sandbox_merchant'] = $is_sandbox; + $this->data['merchant_id'] = sanitize_text_field( $merchant_id ); + $this->data['merchant_email'] = sanitize_email( $merchant_email ); + $this->data['merchant_connected'] = true; + } + + /** + * Whether the currently connected merchant is a sandbox account. + * + * @return bool + */ + public function is_sandbox_merchant() : bool { + return $this->data['sandbox_merchant']; + } + + /** + * Whether the merchant successfully logged into their PayPal account. + * + * @return bool + */ + public function is_merchant_connected() : bool { + return $this->data['merchant_connected'] && $this->data['merchant_id'] && $this->data['merchant_email']; + } + + /** + * Gets the currently connected merchant ID. + * + * @return string + */ + public function get_merchant_id() : string { + return $this->data['merchant_id']; + } + + /** + * Gets the currently connected merchant's email. + * + * @return string + */ + public function get_merchant_email() : string { + return $this->data['merchant_email']; + } } From c97464d7e25cbfe831bcfc56d43af67e9dc518ad Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:07:44 +0100 Subject: [PATCH 53/93] =?UTF-8?q?=E2=9C=A8=20Add=20merchant=20details=20to?= =?UTF-8?q?=20=E2=80=9Ccommon=E2=80=9D=20hydration=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/constants.js | 2 +- .../resources/js/data/common/reducer.js | 24 ++++++++--- .../resources/js/data/common/selectors.js | 3 +- .../src/Endpoint/CommonRestEndpoint.php | 40 ++++++++++++++++++- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/constants.js b/modules/ppcp-settings/resources/js/data/common/constants.js index 4ec4ad20d..60d8512fa 100644 --- a/modules/ppcp-settings/resources/js/data/common/constants.js +++ b/modules/ppcp-settings/resources/js/data/common/constants.js @@ -8,7 +8,7 @@ export const STORE_NAME = 'wc/paypal/common'; /** - * REST path to hydrate data of this module by loading data from the WP DB.. + * REST path to hydrate data of this module by loading data from the WP DB. * * Used by resolvers. * diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 63c231f85..cca47ac6f 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -17,6 +17,13 @@ const defaultTransient = { activities: new Map(), // Read only values, provided by the server via hydrate. + merchant: { + isConnected: false, + isSandbox: false, + id: '', + email: '', + }, + wooSettings: { storeCountry: '', storeCurrency: '', @@ -75,12 +82,17 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { const newState = setPersistent( state, payload.data ); - if ( payload.wooSettings ) { - newState.wooSettings = { - ...newState.wooSettings, - ...payload.wooSettings, - }; - } + // Populate read-only properties. + [ 'wooSettings', 'merchant' ].forEach( ( key ) => { + if ( ! payload[ key ] ) { + return; + } + + newState[ key ] = Object.freeze( { + ...newState[ key ], + ...payload[ key ], + } ); + } ); return newState; }, diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js index 17e422b7a..30d513784 100644 --- a/modules/ppcp-settings/resources/js/data/common/selectors.js +++ b/modules/ppcp-settings/resources/js/data/common/selectors.js @@ -16,7 +16,8 @@ export const persistentData = ( state ) => { }; export const transientData = ( state ) => { - const { data, wooSettings, ...transientState } = getState( state ); + const { data, merchant, wooSettings, ...transientState } = + getState( state ); return transientState || EMPTY_OBJ; }; diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 62ee44ff7..b6fe29d4a 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -61,7 +61,27 @@ class CommonRestEndpoint extends RestEndpoint { ); /** - * Map the internal flags to JS names. + * Map merchant details to JS names. + * + * @var array + */ + private array $merchant_info_map = array( + 'merchant_connected' => array( + 'js_name' => 'isConnected', + ), + 'sandbox_merchant' => array( + 'js_name' => 'isSandbox', + ), + 'merchant_id' => array( + 'js_name' => 'id', + ), + 'merchant_email' => array( + 'js_name' => 'email', + ), + ); + + /** + * Map woo-settings to JS names. * * @var array */ @@ -124,6 +144,7 @@ public function get_details() : WP_REST_Response { ); $extra_data = $this->add_woo_settings( array() ); + $extra_data = $this->add_merchant_info( $extra_data ); return $this->return_success( $js_data, $extra_data ); } @@ -147,6 +168,23 @@ public function update_details( WP_REST_Request $request ) : WP_REST_Response { return $this->get_details(); } + /** + * Appends the "merchant" attribute to the extra_data collection, which + * contains details about the merchant's PayPal account, like the merchant ID. + * + * @param array $extra_data Initial extra_data collection. + * + * @return array Updated extra_data collection. + */ + protected function add_merchant_info( array $extra_data ) : array { + $extra_data['merchant'] = $this->sanitize_for_javascript( + $this->settings->to_array(), + $this->merchant_info_map + ); + + return $extra_data; + } + /** * Appends the "wooSettings" attribute to the extra_data collection to * provide WooCommerce store details, like the store country and currency. From b4d1596fd1747ab91483740e7069d653f72b0b08 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:10:33 +0100 Subject: [PATCH 54/93] =?UTF-8?q?=E2=9C=A8=20New=20action=20to=20refresh?= =?UTF-8?q?=20merchant=20data=20from=20server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/action-types.js | 1 + .../resources/js/data/common/actions.js | 9 +++++++ .../resources/js/data/common/constants.js | 9 +++++++ .../resources/js/data/common/controls.js | 22 +++++++++++++++++ .../resources/js/data/common/reducer.js | 5 ++++ .../src/Endpoint/CommonRestEndpoint.php | 24 +++++++++++++++++++ 6 files changed, 70 insertions(+) diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js index ac2c6db37..34e831508 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -22,4 +22,5 @@ export default { DO_MANUAL_CONNECTION: 'COMMON:DO_MANUAL_CONNECTION', DO_SANDBOX_LOGIN: 'COMMON:DO_SANDBOX_LOGIN', DO_PRODUCTION_LOGIN: 'COMMON:DO_PRODUCTION_LOGIN', + DO_REFRESH_MERCHANT: 'COMMON:DO_REFRESH_MERCHANT', }; diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index abed4f2d3..7dd13206e 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -180,3 +180,12 @@ export const connectViaIdAndSecret = function* () { useSandbox, }; }; + +/** + * Side effect. Clears and refreshes the merchant data via a REST request. + * + * @return {Action} The action. + */ +export const refreshMerchantData = function* () { + return yield { type: ACTION_TYPES.DO_REFRESH_MERCHANT }; +}; diff --git a/modules/ppcp-settings/resources/js/data/common/constants.js b/modules/ppcp-settings/resources/js/data/common/constants.js index 60d8512fa..9499ef069 100644 --- a/modules/ppcp-settings/resources/js/data/common/constants.js +++ b/modules/ppcp-settings/resources/js/data/common/constants.js @@ -16,6 +16,15 @@ export const STORE_NAME = 'wc/paypal/common'; */ export const REST_HYDRATE_PATH = '/wc/v3/wc_paypal/common'; +/** + * REST path to fetch merchant details from the WordPress DB. + * + * Used by controls. + * + * @type {string} + */ +export const REST_HYDRATE_MERCHANT_PATH = '/wc/v3/wc_paypal/common/merchant'; + /** * REST path to persist data of this module to the WP DB. * diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js index 6005385f9..7845f335f 100644 --- a/modules/ppcp-settings/resources/js/data/common/controls.js +++ b/modules/ppcp-settings/resources/js/data/common/controls.js @@ -7,12 +7,15 @@ * @file */ +import { dispatch } from '@wordpress/data'; import apiFetch from '@wordpress/api-fetch'; import { + STORE_NAME, REST_PERSIST_PATH, REST_MANUAL_CONNECTION_PATH, REST_CONNECTION_URL_PATH, + REST_HYDRATE_MERCHANT_PATH, } from './constants'; import ACTION_TYPES from './action-types'; @@ -99,4 +102,23 @@ export const controls = { return result; }, + + async [ ACTION_TYPES.DO_REFRESH_MERCHANT ]() { + let result = null; + + try { + result = await apiFetch( { path: REST_HYDRATE_MERCHANT_PATH } ); + + if ( result.success && result.merchant ) { + await dispatch( STORE_NAME ).hydrate( result ); + } + } catch ( e ) { + result = { + success: false, + error: e, + }; + } + + return result; + }, }; diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index cca47ac6f..5e94a2fa4 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -79,6 +79,11 @@ const commonReducer = createReducer( defaultTransient, defaultPersistent, { return setTransient( state, { activities: newActivities } ); }, + [ ACTION_TYPES.DO_REFRESH_MERCHANT ]: ( state ) => ( { + ...state, + merchant: Object.freeze( { ...defaultTransient.merchant } ), + } ), + [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => { const newState = setPersistent( state, payload.data ); diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index b6fe29d4a..3c0131759 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -130,6 +130,18 @@ public function register_routes() { ), ) ); + + register_rest_route( + $this->namespace, + "/$this->rest_base/merchant", + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_merchant_details' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + ) + ); } /** @@ -168,6 +180,18 @@ public function update_details( WP_REST_Request $request ) : WP_REST_Response { return $this->get_details(); } + /** + * Returns only the (read-only) merchant details from the DB. + * + * @return WP_REST_Response Merchant details. + */ + public function get_merchant_details() : WP_REST_Response { + $js_data = array(); // No persistent data. + $extra_data = $this->add_merchant_info( array() ); + + return $this->return_success( $js_data, $extra_data ); + } + /** * Appends the "merchant" attribute to the extra_data collection, which * contains details about the merchant's PayPal account, like the merchant ID. From 82b364a0acd2678dc1359048c9c77bca859e129d Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:11:17 +0100 Subject: [PATCH 55/93] =?UTF-8?q?=E2=9C=A8=20Add=20=E2=80=9CCommon?= =?UTF-8?q?=E2=80=9D=20hook=20to=20access=20merchant=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/hooks.js | 26 +++++++++++++++++++ .../resources/js/data/common/selectors.js | 4 +++ 2 files changed, 30 insertions(+) diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index de56976e5..8eaaa3924 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -45,6 +45,10 @@ const useHooks = () => { const isSandboxMode = usePersistent( 'useSandbox' ); const isManualConnectionMode = usePersistent( 'useManualConnection' ); + const merchant = useSelect( + ( select ) => select( STORE_NAME ).merchant(), + [] + ); const wooSettings = useSelect( ( select ) => select( STORE_NAME ).wooSettings(), [] @@ -76,6 +80,7 @@ const useHooks = () => { connectToSandbox, connectToProduction, connectViaIdAndSecret, + merchant, wooSettings, }; }; @@ -120,6 +125,27 @@ export const useWooSettings = () => { return wooSettings; }; +export const useMerchantInfo = () => { + const { merchant } = useHooks(); + const { refreshMerchantData } = useDispatch( STORE_NAME ); + + const verifyLoginStatus = useCallback( async () => { + const result = await refreshMerchantData(); + + if ( ! result.success ) { + throw new Error( result?.message || result?.error?.message ); + } + + // Verify if the server state is "connected" and we have a merchant ID. + return merchant?.isConnected && merchant?.id; + }, [ refreshMerchantData, merchant ] ); + + return { + merchant, // Merchant details + verifyLoginStatus, // Callback + }; +}; + // -- Not using the `useHooks()` data provider -- export const useBusyState = () => { diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js index 30d513784..fde5d8c9e 100644 --- a/modules/ppcp-settings/resources/js/data/common/selectors.js +++ b/modules/ppcp-settings/resources/js/data/common/selectors.js @@ -26,6 +26,10 @@ export const getActivityList = ( state ) => { return Object.fromEntries( activities ); }; +export const merchant = ( state ) => { + return getState( state ).merchant || EMPTY_OBJ; +}; + export const wooSettings = ( state ) => { return getState( state ).wooSettings || EMPTY_OBJ; }; From 2b2d5585b1a51dc7fda28ac361ab98ab7c1a9581 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:22:32 +0100 Subject: [PATCH 56/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor:=20Freeze=20re?= =?UTF-8?q?ad-only=20state=20objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/reducer.js | 16 ++++++++-------- .../resources/js/data/onboarding/reducer.js | 17 ++++++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 5e94a2fa4..7d3f5697f 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -12,30 +12,30 @@ import ACTION_TYPES from './action-types'; // Store structure. -const defaultTransient = { +const defaultTransient = Object.freeze( { isReady: false, activities: new Map(), // Read only values, provided by the server via hydrate. - merchant: { + merchant: Object.freeze( { isConnected: false, isSandbox: false, id: '', email: '', - }, + } ), - wooSettings: { + wooSettings: Object.freeze( { storeCountry: '', storeCurrency: '', - }, -}; + } ), +} ); -const defaultPersistent = { +const defaultPersistent = Object.freeze( { useSandbox: false, useManualConnection: false, clientId: '', clientSecret: '', -}; +} ); // Reducer logic. diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index d886a07e8..9dedefc09 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -12,24 +12,24 @@ import ACTION_TYPES from './action-types'; // Store structure. -const defaultTransient = { +const defaultTransient = Object.freeze( { isReady: false, // Read only values, provided by the server. - flags: { + flags: Object.freeze( { canUseCasualSelling: false, canUseVaulting: false, canUseCardPayments: false, - }, -}; + } ), +} ); -const defaultPersistent = { +const defaultPersistent = Object.freeze( { completed: false, step: 0, isCasualSeller: null, // null value will uncheck both options in the UI. areOptionalPaymentMethodsEnabled: null, products: [], -}; +} ); // Reducer logic. @@ -63,7 +63,10 @@ const onboardingReducer = createReducer( defaultTransient, defaultPersistent, { // Flags are not updated by `setPersistent()`. if ( payload.flags ) { - newState.flags = { ...newState.flags, ...payload.flags }; + newState.flags = Object.freeze( { + ...newState.flags, + ...payload.flags, + } ); } return newState; From 620360681cdaa9a067f86c12b6c1819717f53160 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 6 Dec 2024 19:23:22 +0100 Subject: [PATCH 57/93] =?UTF-8?q?=E2=9C=A8=20Integrate=20merchant=20checks?= =?UTF-8?q?=20into=20connections-hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/hooks/useHandleConnections.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js index 10d164cea..d34e74f42 100644 --- a/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js +++ b/modules/ppcp-settings/resources/js/hooks/useHandleConnections.js @@ -23,6 +23,10 @@ const MESSAGES = { 'Could not connect to PayPal. Please make sure your Client ID and Secret Key are correct.', 'woocommerce-paypal-payments' ), + LOGIN_FAILED: __( + 'Login was not successful. Please try again.', + 'woocommerce-paypal-payments' + ), }; const ACTIVITIES = { @@ -63,6 +67,7 @@ const useConnectionBase = () => { const { setCompleted } = OnboardingHooks.useSteps(); const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); + const { verifyLoginStatus } = CommonHooks.useMerchantInfo(); return { handleFailed: ( res, genericMessage ) => { @@ -70,10 +75,18 @@ const useConnectionBase = () => { createErrorNotice( res?.message ?? genericMessage ); }, handleCompleted: async () => { - createSuccessNotice( MESSAGES.CONNECTED ); + try { + const loginSuccessful = await verifyLoginStatus(); - // TODO: Contact the plugin to confirm onboarding is completed. - return setCompleted( true ); + if ( loginSuccessful ) { + createSuccessNotice( MESSAGES.CONNECTED ); + await setCompleted( true ); + } else { + createErrorNotice( MESSAGES.LOGIN_FAILED ); + } + } catch ( error ) { + createErrorNotice( error.message ?? MESSAGES.LOGIN_FAILED ); + } }, createErrorNotice, }; From 390a3f69f859e0117c82de3b9c394732d9398ca4 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Sun, 8 Dec 2024 09:33:49 +0100 Subject: [PATCH 58/93] Update the Settings UI design to match the Figma files --- .../resources/css/_variables.scss | 5 +- .../reusable-components/_button.scss | 1 + .../reusable-components/_fields.scss | 4 +- .../_payment-method-item.scss | 123 ++-- .../_settings-wrapper.scss | 29 +- .../reusable-components/_title-badge.scss | 7 +- .../css/components/screens/_settings.scss | 541 ++++++++++++++++++ .../screens/overview/_tab-overview.scss | 193 ------- .../overview/_tab-payment-methods.scss | 5 - .../screens/overview/_tab-settings.scss | 312 ---------- .../ppcp-settings/resources/css/style.scss | 3 - .../ReusableComponents/ConnectionInfo.js | 65 +-- .../ReusableComponents/PaymentMethodItem.js | 65 --- .../ReusableComponents/SettingsBlock.js | 146 ----- .../SettingsBlocks/AccordionSettingsBlock.js | 56 ++ .../SettingsBlocks/ButtonSettingsBlock.js | 34 ++ .../SettingsBlocks/FeatureSettingsBlock.js | 67 +++ .../SettingsBlocks/InputSettingsBlock.js | 69 +++ .../SettingsBlocks/PaymentMethodItemBlock.js | 65 +++ .../SettingsBlocks/PaymentMethodsBlock.js | 28 + .../SettingsBlocks/RadioSettingsBlock.js | 47 ++ .../SettingsBlocks/SelectSettingsBlock.js | 61 ++ .../SettingsBlocks/SettingsBlock.js | 15 + .../SettingsBlocks/SettingsBlockElements.js | 42 ++ .../SettingsBlocks/TodoSettingsBlock.js | 69 +++ .../SettingsBlocks/ToggleSettingsBlock.js | 37 ++ .../SettingsBlocks/index.js | 20 + .../ReusableComponents/SettingsCard.js | 46 +- .../ReusableComponents/TitleBadge.js | 14 +- .../Screens/Overview/TabOverview.js | 265 ++------- .../Screens/Overview/TabPaymentMethods.js | 36 +- .../Screens/Overview/TabSettings.js | 2 + .../TabSettingsElements/Blocks/OrderIntent.js | 103 ++-- .../Blocks/OtherSettings.js | 63 +- .../Blocks/PaypalSettings.js | 202 +++---- .../TabSettingsElements/Blocks/Sandbox.js | 207 +++---- .../Blocks/SavePaymentMethods.js | 138 ++--- .../Blocks/Troubleshooting.js | 83 +-- .../TabSettingsElements/CommonSettings.js | 27 +- .../TabSettingsElements/ConnectionStatus.js | 53 ++ .../TabSettingsElements/ExpertSettings.js | 54 +- 41 files changed, 1874 insertions(+), 1528 deletions(-) delete mode 100644 modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss delete mode 100644 modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss delete mode 100644 modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss delete mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/PaymentMethodItem.js delete mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/InputSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/RadioSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SelectSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlockElements.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/TodoSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/index.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ConnectionStatus.js diff --git a/modules/ppcp-settings/resources/css/_variables.scss b/modules/ppcp-settings/resources/css/_variables.scss index 613403b67..7be02fb2d 100644 --- a/modules/ppcp-settings/resources/css/_variables.scss +++ b/modules/ppcp-settings/resources/css/_variables.scss @@ -13,7 +13,8 @@ $color-gray-200: #E0E0E0; $color-gray: #646970; $color-text-tertiary: #505050; $color-text-text: #070707; -$color-border:#AEAEAE; +$color-border: #AEAEAE; +$color-divider: #F0F0F0; $shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15); $shadow-selection-box: 0 2px 4px 0 rgba(0, 0, 0, 0.1); @@ -24,6 +25,8 @@ $max-width-onboarding: 1024px; $max-width-onboarding-content: 500px; $max-width-settings: 938px; +$card-vertical-gap: 48px; + #ppcp-settings-container { --max-width-settings: #{$max-width-settings}; --max-width-onboarding: #{$max-width-onboarding}; diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss index 027016760..3528ad71f 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss @@ -18,6 +18,7 @@ button.components-button, a.components-button { &:not(:disabled) { background-color: $color-blueberry; + color: $color-white; } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss index 05418743e..c58f8b021 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_fields.scss @@ -56,13 +56,13 @@ position: relative; label { - @include font(14, 20, 400); + @include font(13, 20, 400); color: $color-gray-800; } } &__radio-description { - @include font(14, 20, 400); + @include font(13, 20, 400); margin: 0; color: $color-gray-800; } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-item.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-item.scss index 3ca44193c..c85c5162e 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-item.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-item.scss @@ -1,75 +1,78 @@ -.ppcp-r-payment-method-item-list { - display: flex; - flex-wrap: wrap; - gap: 16px; -} - -.ppcp-r-payment-method-item { - display: flex; - align-items: flex-start; - width: calc(100% / 3 - 32px / 3); - border: 1px solid $color-gray-300; - padding: 16px; - border-radius: 8px; - min-height: 200px; - - @media screen and (max-width: 767px) { - width: calc(50% - 8px); - } - - @media screen and (max-width: 480px) { - width: 100%; - } - - &__wrap { +.ppcp-r-settings-block__payment-methods { + &.ppcp-r-settings-block { display: flex; - flex-direction: column; - height: 100%; + flex-wrap: wrap; + flex-direction: row; + gap: 16px; } - &__title-wrap { + &__item { display: flex; - align-items: center; - margin: 0 0 8px 0; - gap: 12px; - } + align-items: flex-start; + width: calc(100% / 3 - 32px / 3); + border: 1px solid $color-gray-300; + padding: 16px; + border-radius: 8px; + min-height: 200px; - &__content { - p { - margin: 0; - color: $color-text-tertiary; - @include font(13, 20, 400); + @media screen and (max-width: 767px) { + width: calc(50% - 8px); } - margin: 0 0 12px 0; - } + @media screen and (max-width: 480px) { + width: 100%; + } - &__title { - @include font(13, 20, 500); - color: $color-black; - display: block; - } + &__inner { + display: flex; + flex-direction: column; + height: 100%; + } - &__settings-button { - line-height: 0; - transition: 0.2s ease-out transform; - transform: rotate(0deg); - zoom: 1.005; + &__title-wrapper { + display: flex; + align-items: center; + margin: 0 0 8px 0; + gap: 12px; + } + + &__description { + p { + margin: 0; + color: $color-text-tertiary; + @include font(13, 20, 400); + } - &:hover { - transform: rotate(45deg); - cursor: pointer; + margin: 0 0 12px 0; } - } - button.is-secondary { - @include small-button; - } + &__title { + @include font(13, 20, 500); + color: $color-black; + display: block; + } - &__footer { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: auto; + &__settings { + line-height: 0; + transition: 0.2s ease-out transform; + transform: rotate(0deg); + zoom: 1.005; + + &:hover { + transform: rotate(45deg); + cursor: pointer; + } + } + + button.is-secondary { + @include small-button; + } + + &__footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; + } } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss index dd83011c0..a13df6e77 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-wrapper.scss @@ -16,6 +16,17 @@ } } + &-settings { + > * { + margin-bottom: $card-vertical-gap; + } + + > *:not(:last-child) { + padding-bottom: $card-vertical-gap; + border-bottom: 1px solid $color-gray-200; + } + } + &-settings-card { @media screen and (min-width: 960px) { display: flex; @@ -26,6 +37,12 @@ padding: 24px; } + &__content-wrapper { + display: flex; + flex-direction: column; + gap: 24px; + } + &__header { display: flex; gap: 18px; @@ -43,21 +60,25 @@ } &__content { + border: 1px solid $color-gray-200; + border-radius: 4px; + padding: 24px; @media screen and (min-width: 960px) { flex: 1; } } &__title { - @include font(16, 24, 600); - color: $color-blueberry; + @include font(13, 24, 600); + color: $color-text-text; margin: 0 0 4px 0; display: block; } + &__description { - @include font(14, 20, 400); - color: $color-gray-800; + @include font(13, 20, 400); + color: $color-text-tertiary; margin: 0; } } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_title-badge.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_title-badge.scss index 2abd25541..38429a8f7 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_title-badge.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_title-badge.scss @@ -1,12 +1,11 @@ .ppcp-r-title-badge{ @include font(12, 16, 400); - margin-left:12px; - padding:4px 8px; + padding: 4px 8px; border-radius: 2px; white-space: nowrap; &--positive{ - color:#005C12; - background-color: #EDFAEF; + color: #144722; + background-color: #DAFFE0; } &--negative{ color:#5c0000; diff --git a/modules/ppcp-settings/resources/css/components/screens/_settings.scss b/modules/ppcp-settings/resources/css/components/screens/_settings.scss index c3879a3db..cc8ec5a26 100644 --- a/modules/ppcp-settings/resources/css/components/screens/_settings.scss +++ b/modules/ppcp-settings/resources/css/components/screens/_settings.scss @@ -1,3 +1,4 @@ +// Container and Tab Settings .ppcp-r-tabs.settings, .ppcp-r-container--settings { --max-container-width: var(--max-width-settings); @@ -6,3 +7,543 @@ max-width: var(--max-container-width); } } + +// Todo List and Feature Items +.ppcp-r-tab-overview-todo { + margin: 0 0 48px 0; +} + +.ppcp-r-todo-item { + position: relative; + display: flex; + align-items: center; + gap: 18px; + width: 100%; + + &:not(:last-child) { + border-bottom: 1px solid $color-gray-400; + padding-bottom: 16px; + } + + &:not(:first-child) { + padding-top: 16px; + } + + p { + @include font(14, 20, 400); + } + + &__inner { + position: relative; + display: flex; + align-items: center; + gap: 18px; + } + + &__close { + margin-left: auto; + + &:hover { + cursor: pointer; + color: $color-blueberry; + } + } + + &__description { + @include font(13, 20, 400); + color: $color-blueberry; + } +} + +.ppcp-r-feature-item { + padding-top: 32px; + border-top: 1px solid $color-gray-400; + + &__title { + @include font(16, 20, 600); + color: $color-black; + display: block; + margin: 0 0 8px 0; + } + + &__description { + @include font(14, 20, 400); + color: $color-gray-800; + margin: 0 0 18px 0; + } + + &:not(:last-child) { + padding-bottom: 32px; + } + + &__buttons { + display: flex; + gap: 18px; + } + + &__notes { + display: flex; + flex-direction: column; + + span { + font-weight: 500; + } + } +} + +// Connection Status +.ppcp-r-connection-status { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + + &__status-status { + margin: 0 0 8px 0; + + strong { + @include font(14, 24, 700); + color: $color-black; + } + } + + &__show-all-data { + margin-left: 12px; + } + + &__status-label { + @include font(11, 22, 600); + color: $color-gray-900; + display: block; + text-transform: uppercase; + } + + &__status-value { + @include font(13, 26, 400); + color: $color-text-tertiary; + } + + &__data { + display: flex; + flex-direction: column; + gap: 12px; + } + + &__status-toggle--toggled { + .ppcp-r-connection-status__show-all-data { + transform: rotate(180deg); + } + } + + &__status-row { + display: flex; + flex-direction: column; + * { + user-select: none; + } + strong { + @include font(14, 24, 600); + color: $color-gray-800; + margin-right: 12px; + white-space: nowrap; + } + + .ppcp-r-connection-status__status-toggle { + line-height: 0; + } + &--first { + &:hover { + cursor: pointer; + } + } + } + + @media screen and (max-width: 767px) { + flex-wrap: wrap; + &__status { + width: 100%; + } + &__status-row { + flex-wrap: wrap; + strong { + width: 100%; + } + span { + word-break: break-all; + } + } + } +} + +// Feature Refresh +.ppcp-r-feature-refresh { + display: flex; + gap: 12px; + margin-bottom: 24px; + + &__row { + display: flex; + align-items: center; + } + + &__content { + width: 100%; + + &-title { + @include font(16, 20, 700); + color: $color-black; + display: block; + margin: 0 0 4px 0; + } + + p { + @include font(12, 20, 400); + color: $color-gray-700; + margin: 0; + } + } + + button { + display: flex; + gap: 4px; + @include font(13, 20, 400); + } +} + +// Payment Methods +.ppcp-r-payment-methods { + display: flex; + flex-direction: column; + gap: 48px; +} + +// Settings Card and Block Styles +.ppcp-r-settings-card__content { + > .ppcp-r-settings-block { + &:not(:last-child) { + border-bottom: 1px solid $color-divider; + } + } +} + +.ppcp-r-settings-block { + display: flex; + flex-direction: column; + gap: 16px 0; + + &.ppcp-r-settings-block__input, + &.ppcp-r-settings-block__select { + gap: 6px 0; + } + + .ppcp-r-settings-block__header { + display: flex; + flex-direction: column; + gap: 6px; + + &:not(:last-child):not(.ppcp-r-settings-block--accordion__header) { + padding-bottom: 6px; + } + } + + .ppcp-r-settings-block__title { + @include font(11, 22, 600); + color: $color-gray-900; + display: block; + text-transform: uppercase; + + .ppcp-r-title-badge { + text-transform: none; + margin-left: 6px; + } + } + + .ppcp-r-settings-block__title-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + } + + &.ppcp-r-settings-block__accordion, + &.ppcp-r-settings-block__feature { + .ppcp-r-settings-block__title { + @include font(13, 20, 600); + color: $color-text-text; + text-transform: none; + } + + .ppcp-r-settings-block--accordion__title { + @include font(14, 20, 600); + } + + .ppcp-r-settings-block--accordion__description, + .ppcp-r-settings-block__feature__description { + color: $color-gray-700; + @include font(13, 20, 400); + } + } + + &.ppcp-r-settings-block__toggle { + display: flex; + flex-direction: row; + + .ppcp-r-settings-block__title { + color: $color-text-text; + @include font(13, 20, 400); + text-transform: none; + } + } + + .ppcp-r-settings-block__description { + margin: 0; + @include font(13, 20, 400); + color: $color-gray-800; + + &:not(:last-child) { + padding-bottom: 1em; + } + + a { + color: $color-blueberry; + } + + strong { + color: $color-gray-800; + } + } + + .ppcp-r-settings-block__supplementary-title-label { + @include font(13, 20, 400); + color: $color-text-tertiary; + text-transform: none; + margin-left: 5px; + } + + // Types + &--toggle-content { + &.ppcp-r-settings-block--content-visible { + .ppcp-r-settings-block__toggle-content { + transform: rotate(180deg); + } + } + + .ppcp-r-settings-block__header { + user-select: none; + + &:hover { + cursor: pointer; + } + } + } + + &--sandbox-connected { + .ppcp-r-settings-block__content { + margin-top: 24px; + } + + .ppcp-r-connection-status__data { + margin-bottom: 20px; + } + } + + &--connect-sandbox { + button.components-button { + @include small-button; + } + + .ppcp-r__radio-content-additional { + .ppcp-r-vertical-text-control { + width: 100%; + } + + @include vertical-layout-event-gap(24px); + align-items: flex-start; + + input[type='text'] { + width: 100%; + } + } + } + + &--troubleshooting, + &--settings { + > .ppcp-r-settings-block__content > *:not(:last-child) { + padding-bottom: 32px; + margin-bottom: 32px; + border-bottom: 1px solid $color-gray-500; + } + } + + // Fields + input[type='text'] { + border-color: $color-gray-700; + width: 100%; + max-width: 100%; + color: $color-gray-800; + + &::placeholder { + color: $color-gray-700; + } + } + + // MultiSelect control + .ppcp-r { + &__radio-wrapper { + align-items: flex-start; + gap: 12px; + } + + &__radio-content { + display: flex; + flex-direction: column; + gap: 4px; + + label { + font-weight: 600; + } + } + + &__radio-content-additional { + padding-left: 32px; + } + + // Select control styles + &__control { + border-radius: 2px; + border-color: $color-gray-700; + min-height: auto; + padding: 0; + } + + &__input-container { + padding: 0; + margin: 0; + } + + &__value-container { + padding: 0 0 0 7px; + } + + &__indicator { + padding: 5px; + } + + &__indicator-separator { + display: none; + } + + &__value-container--has-value { + .ppcp-r__single-value { + color: $color-gray-800; + } + } + + &__placeholder, + &__single-value { + @include font(13, 20, 400); + } + + &__option { + &--is-selected { + background-color: $color-gray-200; + } + } + } +} + +// Hooks table +.ppcp-r-table { + &__hooks-url { + width: 70%; + padding-right: 20%; + text-align: left; + vertical-align: top; + } + + &__hooks-events { + vertical-align: top; + text-align: left; + width: 40%; + + span { + display: block; + } + } + + td.ppcp-r-table__hooks-url, + td.ppcp-r-table__hooks-events { + padding-top: 12px; + color: $color-gray-800; + @include font(14, 20, 400); + + span { + color: inherit; + @include font(14, 20, 400); + } + } + + th.ppcp-r-table__hooks-url, + th.ppcp-r-table__hooks-events { + @include font(14, 20, 700); + color: $color-gray-800; + border-bottom: 1px solid $color-gray-600; + padding-bottom: 4px; + } +} + +// Settings specific styles +.ppcp-r-settings-card--common-settings .ppcp-r-settings-card__content, +.ppcp-r-settings-card--expert-settings .ppcp-r-settings-card__content { + > .ppcp-r-settings-block { + &:not(:last-child) { + padding-bottom: 32px; + margin-bottom: 32px; + } + } +} + +.ppcp-r-settings-block { + &--order-intent, + &--save-payment-methods { + @include vertical-layout-event-gap(24px); + + > .ppcp-r-settings-block__content { + @include vertical-layout-event-gap(24px); + } + } +} + +.ppcp-r-settings-block__accordion { + .ppcp-r-settings-block--accordion__header { + gap: 4px; + } + + &.ppcp-r-settings-block--content-visible .ppcp-r-settings-block--accordion__header { + margin-bottom: 24px; + } + + &.ppcp-r-settings-block { + gap: 0; + .ppcp-r-settings-block:not(:last-child) { + &:not(.ppcp-r__radio-content-additional .ppcp-r-settings-block) { + padding-bottom: 32px; + margin-bottom: 32px; + border-bottom: 1px solid $color-divider; + } + } + } +} + +.ppcp-r-settings-block--toggle-content { + .ppcp-r-settings-block__content { + margin-top: 32px; + } +} + +.ppcp-r-settings-block__button { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 50px; +} diff --git a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss deleted file mode 100644 index bffe62134..000000000 --- a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss +++ /dev/null @@ -1,193 +0,0 @@ -.ppcp-r-tab-overview-todo { - margin: 0 0 48px 0; -} - -.ppcp-r-todo-item { - position: relative; - display: flex; - align-items: center; - gap: 18px; - width: 100%; - - &:not(:last-child) { - border-bottom: 1px solid $color-gray-400; - padding-bottom: 24px; - } - - &:not(:first-child) { - padding-top: 24px; - } - - p { - @include font(14, 20, 400); - } - - &__inner { - position: relative; - display: flex; - align-items: center; - gap: 18px; - } - - &__close { - margin-left: auto; - - &:hover { - cursor: pointer; - color: $color-blueberry; - } - } -} - -.ppcp-r-feature-item { - padding-top: 32px; - border-top: 1px solid $color-gray-400; - - &__title { - @include font(16, 20, 600); - color: $color-black; - display: block; - margin: 0 0 8px 0; - } - - &__description { - @include font(14, 20, 400); - color: $color-gray-800; - margin: 0 0 18px 0; - } - - &:not(:last-child) { - padding-bottom: 32px; - } - - &__buttons { - display: flex; - gap: 18px; - } - - &__notes { - display: flex; - flex-direction: column; - - span { - font-weight: 500; - } - } -} - -.ppcp-r-connection-status { - display: flex; - gap: 32px; - padding-bottom: 48px; - margin-bottom: 48px; - border-bottom: 2px solid $color-gray-500; - - &__status-status { - margin: 0 0 8px 0; - - strong { - @include font(14, 24, 700); - color: $color-black; - } - } - - &__show-all-data { - margin-left: 12px; - } - - &__status-label { - span { - @include font(12, 16, 400); - color: $color-gray-700; - } - } - - &__data { - display: flex; - flex-direction: column; - gap: 12px; - } - - &__status-toggle--toggled{ - .ppcp-r-connection-status__show-all-data{ - transform:rotate(180deg); - } - } - - &__status-row { - display: flex; - align-items: center; - *{ - user-select: none; - } - strong { - @include font(14, 24, 600); - color: $color-gray-800; - margin-right: 12px; - white-space: nowrap; - } - - span:not(.ppcp-r-connection-status__status-toggle) { - @include font(14, 24, 400); - color: $color-gray-800; - } - .ppcp-r-connection-status__status-toggle{ - line-height: 0; - } - &--first{ - &:hover{ - cursor: pointer; - } - } - } - - @media screen and (max-width: 767px) { - flex-wrap: wrap; - &__status { - width: 100%; - } - &__status-row { - flex-wrap: wrap; - strong{ - width: 100%; - } - span{ - word-break:break-all; - } - } - } -} - -.ppcp-r-feature-refresh { - display: flex; - gap: 12px; - margin-bottom: 24px; - - &__row { - display: flex; - align-items: center; - } - - &__content { - width: 100%; - - &-title { - @include font(16, 20, 700); - color: $color-black; - display: block; - margin: 0 0 4px 0; - } - - p { - @include font(12, 20, 400); - color: $color-gray-700; - margin: 0; - } - } - - button { - display: flex; - gap: 4px; - @include font(13, 20, 400); - } -} diff --git a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss deleted file mode 100644 index 556589d03..000000000 --- a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss +++ /dev/null @@ -1,5 +0,0 @@ -.ppcp-r-payment-methods{ - display: flex; - flex-direction: column; - gap:48px; -} diff --git a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss deleted file mode 100644 index 197d575ea..000000000 --- a/modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss +++ /dev/null @@ -1,312 +0,0 @@ -// Global settings styles -.ppcp-r-settings { - @include vertical-layout-event-gap(48px); -} - - -.ppcp-r-settings-card__content { - > .ppcp-r-settings-block { - &:not(:last-child) { - border-bottom: 1.5px solid $color-gray-700; - } - } -} - -.ppcp-r-settings-block { - .ppcp-r-settings-block__header { - display: flex; - gap: 48px; - - &-inner { - display: flex; - flex-direction: column; - gap: 4px; - } - } - - &__action { - margin-left: auto; - } - - &--primary { - > .ppcp-r-settings-block__header { - .ppcp-r-settings-block__title { - @include font(16, 20, 700); - color: $color-black; - } - } - } - - &--secondary { - > .ppcp-r-settings-block__header { - .ppcp-r-settings-block__title { - @include font(16, 20, 600); - color: $color-gray-800; - } - } - } - - &--tertiary { - padding-bottom: 0; - margin-bottom: 24px; - - > .ppcp-r-settings-block__header { - align-items: center; - - .ppcp-r-settings-block__title { - color: $color-gray-800; - @include font(14, 20, 400); - } - } - } - - .ppcp-r-settings-block__description { - margin: 0; - @include font(14, 20, 400); - color: $color-gray-800; - - a { - color: $color-blueberry; - } - - strong { - color: $color-gray-800; - } - } - - // Types - &--toggle-content { - &.ppcp-r-settings-block--content-visible { - .ppcp-r-settings-block__toggle-content { - transform: rotate(180deg); - } - } - - .ppcp-r-settings-block__header { - user-select: none; - - &:hover { - cursor: pointer; - } - } - } - - &--sandbox-connected { - .ppcp-r-settings-block__content { - margin-top: 24px; - } - - button.is-secondary { - @include small-button; - } - - .ppcp-r-connection-status__data { - margin-bottom: 20px; - } - } - - &--expert-rdb{ - @include vertical-layout-event-gap(24px); - } - &--connect-sandbox { - button.components-button { - @include small-button; - } - - .ppcp-r__radio-content-additional { - .ppcp-r-vertical-text-control { - width: 100%; - } - - @include vertical-layout-event-gap(24px); - align-items: flex-start; - - input[type='text'] { - width: 100%; - } - } - } - - &--troubleshooting { - > .ppcp-r-settings-block__content > *:not(:last-child) { - padding-bottom: 32px; - margin-bottom: 32px; - border-bottom: 1px solid $color-gray-500; - } - } - - &--settings{ - > .ppcp-r-settings-block__content > *:not(:last-child){ - padding-bottom: 32px; - margin-bottom: 32px; - border-bottom: 1px solid $color-gray-500; - } - } - - // Fields - input[type='text'] { - border-color: $color-gray-700; - width: 282px; - max-width: 100%; - color: $color-gray-800; - } - - input[type='text'] { - &::placeholder { - color: $color-gray-700; - } - } - - .ppcp-r { - &__radio-wrapper { - align-items: flex-start; - gap: 12px; - } - - &__radio-content { - display: flex; - flex-direction: column; - gap: 4px; - - label { - font-weight: 600; - } - } - - &__radio-content-additional { - padding-left: 32px; - } - } - - // MultiSelect control - .ppcp-r { - &__control { - border-radius: 2px; - border-color: $color-gray-700; - width: 282px; - min-height: auto; - padding: 0; - } - - &__input-container { - padding: 0; - margin: 0; - } - - &__value-container { - padding: 0 0 0 7px; - } - - &__indicator { - padding: 5px; - } - - &__indicator-separator { - display: none; - } - - &__value-container--has-value { - .ppcp-r__single-value { - color: $color-gray-800; - } - } - - &__placeholde, &__single-value { - @include font(13, 20, 400); - } - - &__option { - &--is-selected { - background-color: $color-gray-200; - } - } - } -} - -// Special settings styles - -// Hooks table -.ppcp-r-table { - &__hooks-url { - width: 70%; - padding-right: 20%; - text-align: left; - vertical-align: top; - } - - &__hooks-events { - vertical-align: top; - text-align: left; - width: 40%; - - span { - display: block; - } - } - - td.ppcp-r-table__hooks-url, td.ppcp-r-table__hooks-events { - padding-top: 12px; - color: $color-gray-800; - @include font(14, 20, 400); - - span { - color: inherit; - @include font(14, 20, 400); - } - } - - th.ppcp-r-table__hooks-url, th.ppcp-r-table__hooks-events { - @include font(14, 20, 700); - color: $color-gray-800; - border-bottom: 1px solid $color-gray-600; - padding-bottom: 4px; - } -} - -// Common settings have 48px margin&padding bottom between blocks -.ppcp-r-settings-card--common-settings .ppcp-r-settings-card__content { - > .ppcp-r-settings-block { - &:not(:last-child) { - padding-bottom: 48px; - margin-bottom: 48px; - } - } -} - -// Expert settings have 32px margin&padding bottom between blocks -.ppcp-r-settings-card--expert-settings .ppcp-r-settings-card__content { - > .ppcp-r-settings-block { - &:not(:last-child) { - padding-bottom: 32px; - margin-bottom: 32px; - } - } -} - - -// Order intent block has 32px gap and no lines in between -// Save payment methods block has 32px gap and no lines in between -.ppcp-r-settings-block { - &--order-intent, &--save-payment-methods { - @include vertical-layout-event-gap(32px); - - > .ppcp-r-settings-block__content { - @include vertical-layout-event-gap(32px); - } - } -} - - -// Most primary settings block in the expert settings have 32px space after description -.ppcp-r-settings-block--toggle-content { - .ppcp-r-settings-block__content { - margin-top: 32px; - } -} - -// Common settings have actions aligned top with the text, Expert settings have actions alligned middle with the text -.ppcp-r-settings-card--expert-settings { - .ppcp-r-settings-block__header { - align-items: center; - } -} diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index 72a913fdf..8358238df 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -21,9 +21,6 @@ @import './components/reusable-components/welcome-docs'; @import './components/screens/onboarding'; @import './components/screens/settings'; - @import './components/screens/overview/tab-overview'; - @import './components/screens/overview/tab-payment-methods'; - @import './components/screens/overview/tab-settings'; } @import './components/reusable-components/payment-method-modal'; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/ConnectionInfo.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/ConnectionInfo.js index bfa45013e..5fbfdb1f1 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/ConnectionInfo.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/ConnectionInfo.js @@ -1,5 +1,4 @@ import { __ } from '@wordpress/i18n'; -import data from '../../utils/data'; import { useState } from '@wordpress/element'; const ConnectionInfo = ( { connectionStatusDataDefault } ) => { @@ -7,13 +6,6 @@ const ConnectionInfo = ( { connectionStatusDataDefault } ) => { ...connectionStatusDataDefault, } ); - const showAllData = () => { - setConnectionData( { - ...connectionData, - showAllData: ! connectionData.showAllData, - } ); - }; - const toggleStatusClassName = [ 'ppcp-r-connection-status__status-toggle' ]; if ( connectionData.showAllData ) { @@ -24,43 +16,30 @@ const ConnectionInfo = ( { connectionStatusDataDefault } ) => { return (
-
showAllData() } - > - - { __( 'Email address:', 'woocommerce-paypal-payments' ) } - - { connectionData.email } - - { data().getImage( - 'icon-arrow-down.svg', - 'ppcp-r-connection-status__show-all-data' - ) } +
+ + { __( 'Merchant ID', 'woocommerce-paypal-payments' ) } + + + { connectionData.merchantId } + +
+
+ + { __( 'Email address', 'woocommerce-paypal-payments' ) } + + + { connectionData.email } + +
+
+ + { __( 'Client ID', 'woocommerce-paypal-payments' ) } + + + { connectionData.clientId }
- { connectionData.showAllData && ( - <> -
- - { __( - 'Merchant ID:', - 'woocommerce-paypal-payments' - ) } - - { connectionData.merchantId } -
-
- - { __( - 'Client ID:', - 'woocommerce-paypal-payments' - ) } - - { connectionData.clientId } -
- - ) }
); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PaymentMethodItem.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PaymentMethodItem.js deleted file mode 100644 index dbe9c6971..000000000 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PaymentMethodItem.js +++ /dev/null @@ -1,65 +0,0 @@ -import { Button } from '@wordpress/components'; -import PaymentMethodIcon from './PaymentMethodIcon'; -import { PayPalCheckbox } from './Fields'; -import { useState } from '@wordpress/element'; -import { ToggleControl } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import data from '../../utils/data'; - -const PaymentMethodItem = ( props ) => { - const [ paymentMethodState, setPaymentMethodState ] = useState(); - const [ modalIsVisible, setModalIsVisible ] = useState( false ); - let Modal = null; - if ( props?.modal ) { - Modal = props.modal; - } - const handleCheckboxState = ( checked ) => { - if ( checked ) { - setPaymentMethodState( props.payment_method_id ); - } else { - setPaymentMethodState( null ); - } - }; - return ( - <> -
-
-
- - - { props.title } - -
-
-

{ props.description }

-
-
- - handleCheckboxState( newValue ) - } - /> -
setModalIsVisible( true ) } - > - { Modal && data().getImage( 'icon-settings.svg' ) } -
-
-
-
- { Modal && modalIsVisible && ( - - ) } - - ); -}; - -export default PaymentMethodItem; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js deleted file mode 100644 index d23860b38..000000000 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlock.js +++ /dev/null @@ -1,146 +0,0 @@ -import { Button, ToggleControl, TextControl } from '@wordpress/components'; -import data from '../../utils/data'; -import { useState } from '@wordpress/element'; -import Select, { components } from 'react-select'; - -export const SETTINGS_BLOCK_TYPE_EMPTY = 'empty'; -export const SETTINGS_BLOCK_TYPE_TOGGLE = 'toggle'; -export const SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT = 'toggle-content'; -export const SETTINGS_BLOCK_TYPE_INPUT = 'input'; -export const SETTINGS_BLOCK_TYPE_BUTTON = 'button'; -export const SETTINGS_BLOCK_TYPE_SELECT = 'select'; - -export const SETTINGS_BLOCK_STYLING_TYPE_PRIMARY = 'primary'; -export const SETTINGS_BLOCK_STYLING_TYPE_SECONDARY = 'secondary'; -export const SETTINGS_BLOCK_STYLING_TYPE_TERTIARY = 'tertiary'; - -const SettingsBlock = ( { - className, - title, - description, - children, - style, - actionProps, - tag, -} ) => { - const [ toggleContentVisible, setToggleContentVisible ] = useState( - actionProps?.type !== SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT - ); - - const toggleContent = () => { - if ( actionProps?.type !== SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT ) { - return; - } - setToggleContentVisible( ! toggleContentVisible ); - }; - - const blockClassName = [ 'ppcp-r-settings-block' ]; - - blockClassName.push( 'ppcp-r-settings-block--' + style ); - blockClassName.push( 'ppcp-r-settings-block--' + actionProps?.type ); - - if ( className ) { - blockClassName.push( className ); - } - - if ( - toggleContentVisible && - actionProps?.type === SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT - ) { - blockClassName.push( 'ppcp-r-settings-block--content-visible' ); - } - - return ( -
-
toggleContent() } - > -
- - { title } - { tag && tag } - -

-

- { actionProps?.type !== SETTINGS_BLOCK_TYPE_EMPTY && ( -
- { actionProps?.type === SETTINGS_BLOCK_TYPE_TOGGLE && ( - - actionProps?.callback( - actionProps?.key, - newValue - ) - } - /> - ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_INPUT && ( - <> - - actionProps?.callback( - actionProps?.key, - newValue - ) - } - /> - - ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_BUTTON && ( - - ) } - { actionProps?.type === - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT && ( -
- { data().getImage( 'icon-arrow-down.svg' ) } -
- ) } - { actionProps?.type === SETTINGS_BLOCK_TYPE_SELECT && ( - + + ), + description: ( { description } ) => ( + { description } + ), +}; + +const SelectSettingsBlock = ( { + title, + description, + order = DEFAULT_ELEMENT_ORDER, + ...props +} ) => ( + ( + <> + { order.map( ( elementKey ) => { + const RenderElement = ELEMENT_RENDERERS[ elementKey ]; + return RenderElement ? ( + + ) : null; + } ) } + + ), + ] } + /> +); + +export default SelectSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js new file mode 100644 index 000000000..5e4985104 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js @@ -0,0 +1,15 @@ +const SettingsBlock = ( { className, components = [] } ) => { + const blockClassName = [ 'ppcp-r-settings-block', className ].filter( + Boolean + ); + + return ( +
+ { components.map( ( Component, index ) => ( + + ) ) } +
+ ); +}; + +export default SettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlockElements.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlockElements.js new file mode 100644 index 000000000..296c2c2ad --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlockElements.js @@ -0,0 +1,42 @@ +// Block Elements +export const Title = ( { children, className = '' } ) => ( + + { children } + +); +export const TitleWrapper = ( { children } ) => ( + { children } +); + +export const SupplementaryLabel = ( { children } ) => ( + + { children } + +); + +export const Description = ( { children, className = '' } ) => ( + + { children } + +); + +export const Action = ( { children } ) => ( +
{ children }
+); + +export const Header = ( { children, className = '' } ) => ( +
+ { children } +
+); + +// Card Elements +export const Content = ( { children } ) => ( +
{ children }
+); + +export const ContentWrapper = ( { children } ) => ( +
{ children }
+); diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/TodoSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/TodoSettingsBlock.js new file mode 100644 index 000000000..4f9b01644 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/TodoSettingsBlock.js @@ -0,0 +1,69 @@ +import { PayPalCheckbox, handleCheckboxState } from '../Fields'; +import data from '../../../utils/data'; + +const TodoSettingsBlock = ( { + todos, + setTodos, + todosData, + setTodosData, + className = '', +} ) => { + if ( todosData.length === 0 ) { + return null; + } + + return ( +
+ { todosData.map( ( todo ) => ( + + ) ) } +
+ ); +}; + +const TodoItem = ( props ) => { + return ( +
+
+ +
+ { props.description } +
+
+
+ removeTodo( + props.value, + props.todosData, + props.changeTodos + ) + } + > + { data().getImage( 'icon-close.svg' ) } +
+
+ ); +}; + +const removeTodo = ( todoValue, todosData, changeTodos ) => { + changeTodos( todosData.filter( ( todo ) => todo.value !== todoValue ) ); +}; + +export default TodoSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js new file mode 100644 index 000000000..3e0c0eac6 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js @@ -0,0 +1,37 @@ +import { ToggleControl } from '@wordpress/components'; +import SettingsBlock from './SettingsBlock'; +import { Header, Title, Action, Description } from './SettingsBlockElements'; + +const ToggleSettingsBlock = ( { title, description, ...props } ) => ( + ( + + + props.actionProps?.callback( + props.actionProps?.key, + newValue + ) + } + /> + + ), + () => ( +
+ { title && { title } } + { description && ( + { description } + ) } +
+ ), + ] } + /> +); + +export default ToggleSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/index.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/index.js new file mode 100644 index 000000000..80a5db448 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/index.js @@ -0,0 +1,20 @@ +export { default as SettingsBlock } from './SettingsBlock'; +export { default as ButtonSettingsBlock } from './ButtonSettingsBlock'; +export { default as InputSettingsBlock } from './InputSettingsBlock'; +export { default as SelectSettingsBlock } from './SelectSettingsBlock'; +export { default as AccordionSettingsBlock } from './AccordionSettingsBlock'; +export { default as ToggleSettingsBlock } from './ToggleSettingsBlock'; +export { default as RadioSettingsBlock } from './RadioSettingsBlock'; +export { default as PaymentMethodsBlock } from './PaymentMethodsBlock'; +export { default as PaymentMethodItemBlock } from './PaymentMethodItemBlock'; + +export { + Title, + TitleWrapper, + SupplementaryLabel, + Description, + Action, + Content, + ContentWrapper, + Header, +} from './SettingsBlockElements'; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js index 11693d172..aeb0e3561 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js @@ -1,26 +1,50 @@ -import data from '../../utils/data'; +import { Content, ContentWrapper } from './SettingsBlocks'; -const SettingsCard = ( props ) => { - let className = 'ppcp-r-settings-card'; +const SettingsCard = ( { + className: extraClassName, + title, + description, + children, + contentItems, + contentContainer = true, +} ) => { + const className = [ 'ppcp-r-settings-card', extraClassName ] + .filter( Boolean ) + .join( ' ' ); + + const renderContent = () => { + // If contentItems array is provided, wrap each item in Content component + if ( contentItems ) { + return ( + + { contentItems.map( ( item, index ) => ( + { item } + ) ) } + + ); + } + + // Otherwise handle regular children with contentContainer prop + if ( contentContainer ) { + return { children }; + } + + return children; + }; - if ( props?.className ) { - className += ' ' + props.className; - } return (
- { props.title } + { title }

- { props.description } + { description }

-
- { props.children } -
+ { renderContent() }
); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TitleBadge.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TitleBadge.js index dc9cdad71..9b493e735 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TitleBadge.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TitleBadge.js @@ -1,11 +1,13 @@ const TitleBadge = ( { text, type } ) => { const className = 'ppcp-r-title-badge ' + `ppcp-r-title-badge--${ type }`; - return ; + return ( + + ); }; export const TITLE_BADGE_POSITIVE = 'positive'; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js index 6e913642f..fe3e64218 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js @@ -1,19 +1,11 @@ -import SettingsCard from '../../ReusableComponents/SettingsCard'; import { __ } from '@wordpress/i18n'; -import { - PayPalCheckbox, - handleCheckboxState, -} from '../../ReusableComponents/Fields'; import { useState } from '@wordpress/element'; -import data from '../../../utils/data'; import { Button } from '@wordpress/components'; -import TitleBadge, { - TITLE_BADGE_NEGATIVE, - TITLE_BADGE_POSITIVE, -} from '../../ReusableComponents/TitleBadge'; -import ConnectionInfo, { - connectionStatusDataDefault, -} from '../../ReusableComponents/ConnectionInfo'; +import SettingsCard from '../../ReusableComponents/SettingsCard'; +import TodoSettingsBlock from '../../ReusableComponents/SettingsBlocks/TodoSettingsBlock'; +import FeatureSettingsBlock from '../../ReusableComponents/SettingsBlocks/FeatureSettingsBlock'; +import { TITLE_BADGE_POSITIVE } from '../../ReusableComponents/TitleBadge'; +import data from '../../../utils/data'; const TabOverview = () => { const [ todos, setTodos ] = useState( [] ); @@ -33,200 +25,52 @@ const TabOverview = () => { 'woocommerce-paypal-payments' ) } > -
- { todosData.map( ( todo ) => ( - - ) ) } -
+ ) } - - - - { featuresDefault.map( ( feature ) => { - return ( - - ); - } ) } - -
- ); -}; -const ConnectionStatus = ( { connectionData } ) => { - return ( -
-
-
- - { __( 'Connection', 'woocommerce-paypal-payments' ) } - - { connectionData.connectionStatus ? ( - - ) : ( - - ) } -
-
- - { __( - 'PayPal Account Details', - 'woocommerce-paypal-payments' - ) } - -
-
- { connectionData.connectionStatus && ( - - ) } -
- ); -}; - -const FeaturesRefresh = () => { - return ( -
-
- - { __( 'Features', 'woocommerce-paypal-payments' ) } - -

- { __( - 'After making changes to your PayPal account, click Refresh to update your store features.', - 'woocommerce-paypal-payments' - ) } -

-
- -
- ); -}; - -const TodoItem = ( props ) => { - return ( -
-
- { ' ' } -

{ props.description }

-
-
- removeTodo( - props.value, - props.todosData, - props.changeTodos - ) + +

{ __( 'Enable additional features…' ) }

+

{ __( 'Click Refresh…' ) }

+ +
} - > - { data().getImage( 'icon-close.svg' ) } -
-
- ); -}; - -const FeatureItem = ( { feature } ) => { - const printNotes = () => { - if ( ! feature?.notes ) { - return null; - } - - if ( Array.isArray( feature.notes ) && feature.notes.length === 0 ) { - return null; - } - - return ( - <> -
-
- - { feature.notes.map( ( note, index ) => { - return { note }; - } ) } - - - ); - }; - - return ( -
- - { feature.title } - { feature?.featureStatus && ( - ( + - ) } - -

- { feature.description } - { printNotes() } -

-
- { feature.buttons.map( ( button ) => { - return ( - - ); - } ) } -
+ ) ) } + />
); }; -const removeTodo = ( todoValue, todosData, changeTodos ) => { - changeTodos( todosData.filter( ( todo ) => todo.value !== todoValue ) ); -}; - const todosDataDefault = [ { value: 'paypal_later_messaging', @@ -272,12 +116,12 @@ const featuresDefault = [ ), buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Configure', 'woocommerce-paypal-payments' ), url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, @@ -296,12 +140,12 @@ const featuresDefault = [ ), buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Configure', 'woocommerce-paypal-payments' ), url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, @@ -319,12 +163,12 @@ const featuresDefault = [ ), buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Apply', 'woocommerce-paypal-payments' ), url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, @@ -340,12 +184,12 @@ const featuresDefault = [ featureStatus: true, buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Configure', 'woocommerce-paypal-payments' ), url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, @@ -363,7 +207,7 @@ const featuresDefault = [ ), buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Domain registration', 'woocommerce-paypal-payments' @@ -371,7 +215,7 @@ const featuresDefault = [ url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, @@ -386,16 +230,17 @@ const featuresDefault = [ ), buttons: [ { - type: 'primary', + type: 'secondary', text: __( 'Configure', 'woocommerce-paypal-payments' ), url: '#', }, { - type: 'secondary', + type: 'tertiary', text: __( 'Learn more', 'woocommerce-paypal-payments' ), url: '#', }, ], }, ]; + export default TabOverview; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js index 453a34426..6185f1ad8 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js @@ -1,24 +1,11 @@ -import SettingsCard from '../../ReusableComponents/SettingsCard'; import { __ } from '@wordpress/i18n'; -import PaymentMethodItem from '../../ReusableComponents/PaymentMethodItem'; +import SettingsCard from '../../ReusableComponents/SettingsCard'; +import PaymentMethodsBlock from '../../ReusableComponents/SettingsBlocks/PaymentMethodsBlock'; import ModalPayPal from './Modals/ModalPayPal'; import ModalFastlane from './Modals/ModalFastlane'; import ModalAcdc from './Modals/ModalAcdc'; const TabPaymentMethods = () => { - const renderPaymentMethods = ( data ) => { - return ( -
- { data.map( ( paymentMethod ) => ( - - ) ) } -
- ); - }; - return (
{ 'woocommerce-paypal-payments' ) } icon="icon-checkout-standard.svg" + contentContainer={ false } > - { renderPaymentMethods( paymentMethodsPayPalCheckoutDefault ) } + { 'woocommerce-paypal-payments' ) } icon="icon-checkout-online-methods.svg" + contentContainer={ false } > - { renderPaymentMethods( - paymentMethodsOnlineCardPaymentsDefault - ) } + { 'woocommerce-paypal-payments' ) } icon="icon-checkout-alternative-methods.svg" + contentContainer={ false } > - { renderPaymentMethods( paymentMethodsAlternativeDefault ) } +
); @@ -124,7 +118,7 @@ const paymentMethodsOnlineCardPaymentsDefault = [ id: 'fastlane', title: __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ), description: __( - 'Tap into the scale and trust of PayPal’s customer network to recognize shoppers and make guest checkout more seamless than ever.', + "Tap into the scale and trust of PayPal's customer network to recognize shoppers and make guest checkout more seamless than ever.", 'woocommerce-paypal-payments' ), icon: 'payment-method-fastlane', diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettings.js index b992d6728..d37e9c721 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettings.js @@ -1,4 +1,5 @@ import { useState } from '@wordpress/element'; +import ConnectionStatus from './TabSettingsElements/ConnectionStatus'; import CommonSettings from './TabSettingsElements/CommonSettings'; import ExpertSettings from './TabSettingsElements/ExpertSettings'; @@ -31,6 +32,7 @@ const TabSettings = () => { return ( <>
+ { return ( - + components={ [ + () => ( + <> +
+ + { __( + 'Order Intent', + 'woocommerce-paypal-payments' + ) } + + + { __( + 'Choose between immediate capture or authorization-only, with manual capture in the Order section.', + 'woocommerce-paypal-payments' + ) } + +
+ + ), + () => ( + <> + - - + + + ), + ] } + /> ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OtherSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OtherSettings.js index 84bea84c8..a377f0217 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OtherSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OtherSettings.js @@ -1,66 +1,59 @@ -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_SELECT, - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, -} from '../../../../ReusableComponents/SettingsBlock'; import { __ } from '@wordpress/i18n'; +import { + AccordionSettingsBlock, + SelectSettingsBlock, +} from '../../../../ReusableComponents/SettingsBlocks'; + +const creditCardExamples = [ + { value: '', label: __( 'Select', 'woocommerce-paypal-payments' ) }, + { + value: 'mastercard', + label: __( 'Mastercard', 'woocommerce-paypal-payments' ), + }, + { value: 'visa', label: __( 'Visa', 'woocommerce-paypal-payments' ) }, + { + value: 'amex', + label: __( 'American Express', 'woocommerce-paypal-payments' ), + }, + { value: 'jcb', label: __( 'JCB', 'woocommerce-paypal-payments' ) }, + { + value: 'diners-club', + label: __( 'Diners Club', 'woocommerce-paypal-payments' ), + }, +]; const OtherSettings = ( { settings, updateFormValue } ) => { return ( - - - + ); }; -const creditCardExamples = [ - { value: '', label: __( 'Select', 'woocommerce-paypal-payments' ) }, - { - value: 'mastercard', - label: __( 'Mastercard', 'woocommerce-paypal-payments' ), - }, - { value: 'visa', label: __( 'Visa', 'woocommerce-paypal-payments' ) }, - { - value: 'amex', - label: __( 'American Express', 'woocommerce-paypal-payments' ), - }, - { value: 'jcb', label: __( 'JCB', 'woocommerce-paypal-payments' ) }, - { - value: 'diners-club', - label: __( 'Diners Club', 'woocommerce-paypal-payments' ), - }, -]; - export default OtherSettings; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaypalSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaypalSettings.js index 7b01ea203..f8d68881e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaypalSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/PaypalSettings.js @@ -1,33 +1,28 @@ import { __ } from '@wordpress/i18n'; -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_EMPTY, - SETTINGS_BLOCK_TYPE_INPUT, - SETTINGS_BLOCK_TYPE_SELECT, - SETTINGS_BLOCK_TYPE_TOGGLE, - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, -} from '../../../../ReusableComponents/SettingsBlock'; -import { PayPalRdbWithContent } from '../../../../ReusableComponents/Fields'; +import { + AccordionSettingsBlock, + RadioSettingsBlock, + ToggleSettingsBlock, + InputSettingsBlock, + SelectSettingsBlock, +} from '../../../../ReusableComponents/SettingsBlocks'; const PaypalSettings = ( { updateFormValue, settings } ) => { return ( - - { 'Due to differences in how WooCommerce and PayPal calculates taxes, some transactions may fail due to a rounding error. This settings determines the fallback behavior.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_EMPTY, - } } - > -
- - updateFormValue( - 'subtotalMismatchFallback', - newValue - ) - } - label={ __( + options={ [ + { + id: 'add_a_correction', + value: 'add_a_correction', + label: __( 'Add a correction', 'woocommerce-paypal-payments' - ) } - description={ __( + ), + description: __( 'Adds an additional line item with the missing amount.', 'woocommerce-paypal-payments' - ) } - /> - - updateFormValue( - 'subtotalMismatchFallback', - newValue - ) - } - label={ __( + ), + }, + { + id: 'do_not_send_line_items', + value: 'do_not_send_line_items', + label: __( 'Do not send line items', 'woocommerce-paypal-payments' - ) } - description={ __( - 'Resubmit the transaction without line item details', + ), + description: __( + 'Resubmit the transaction without line item details.', 'woocommerce-paypal-payments' - ) } - /> -
-
+ ), + }, + ] } + actionProps={ { + name: 'paypal_settings_mismatch', + key: 'subtotalMismatchFallback', + currentValue: settings.subtotalMismatchFallback, + callback: updateFormValue, + } } + /> - { 'If enabled, PayPal will not allow buyers to use funding sources that take additional time to complete, such as eChecks.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } actionProps={ { - type: SETTINGS_BLOCK_TYPE_TOGGLE, value: settings.savePaypalAndVenmo, callback: updateFormValue, key: 'savePaypalAndVenmo', } } /> - { 'woocommerce-paypal-payments' ), } } + order={ [ 'title', 'description', 'action' ] } /> - { 'woocommerce-paypal-payments' ), } } + order={ [ 'title', 'description', 'action' ] } /> - { 'Determine which experience a buyer sees when they click the PayPal button.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_EMPTY, - } } - > -
- - updateFormValue( 'paypalLandingPage', newValue ) - } - label={ __( + options={ [ + { + id: 'no_preference', + value: 'no_reference', + label: __( 'No preference', 'woocommerce-paypal-payments' - ) } - description={ __( + ), + description: __( 'Shows the buyer the PayPal login for a recognized PayPal buyer.', 'woocommerce-paypal-payments' - ) } - /> - - updateFormValue( 'paypalLandingPage', newValue ) - } - label={ __( + ), + }, + { + id: 'login_page', + value: 'login_page', + label: __( 'Login page', 'woocommerce-paypal-payments' - ) } - description={ __( + ), + description: __( 'Always show the buyer the PayPal login screen.', 'woocommerce-paypal-payments' - ) } - /> - - updateFormValue( 'paypalLandingPage', newValue ) - } - label={ __( + ), + }, + { + id: 'guest_checkout_page', + value: 'guest_checkout_page', + label: __( 'Guest checkout page', 'woocommerce-paypal-payments' - ) } - description={ __( + ), + description: __( 'Always show the buyer the guest checkout fields first.', 'woocommerce-paypal-payments' - ) } - /> -
-
- + + { 'woocommerce-paypal-payments' ), } } + order={ [ 'title', 'description', 'action' ] } /> - + ); }; @@ -235,4 +200,5 @@ const languagesExample = [ { value: 'es', label: 'Spanish' }, { value: 'it', label: 'Italian' }, ]; + export default PaypalSettings; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Sandbox.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Sandbox.js index f47711098..93a4a7d0d 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Sandbox.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Sandbox.js @@ -1,43 +1,40 @@ import { __, sprintf } from '@wordpress/i18n'; -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_STYLING_TYPE_TERTIARY, - SETTINGS_BLOCK_TYPE_EMPTY, - SETTINGS_BLOCK_TYPE_TOGGLE, - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, -} from '../../../../ReusableComponents/SettingsBlock'; +import { Button } from '@wordpress/components'; +import { + AccordionSettingsBlock, + ButtonSettingsBlock, + RadioSettingsBlock, + ToggleSettingsBlock, + InputSettingsBlock, +} from '../../../../ReusableComponents/SettingsBlocks'; import TitleBadge, { TITLE_BADGE_POSITIVE, } from '../../../../ReusableComponents/TitleBadge'; import ConnectionInfo, { connectionStatusDataDefault, } from '../../../../ReusableComponents/ConnectionInfo'; -import { Button, TextControl } from '@wordpress/components'; -import { PayPalRdbWithContent } from '../../../../ReusableComponents/Fields'; const Sandbox = ( { settings, updateFormValue } ) => { const className = settings.sandboxConnected ? 'ppcp-r-settings-block--sandbox-connected' : 'ppcp-r-settings-block--sandbox-disconnected'; + return ( - Note: No real payments/money movement occur in Sandbox mode. Do not ship orders made in this mode.", + "Test your site in PayPal's Sandbox environment.", 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_PRIMARY } actionProps={ { - type: SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, callback: updateFormValue, key: 'payNowExperience', value: settings.payNowExperience, } } > { settings.sandboxConnected && ( - { ) } /> } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_EMPTY, - callback: updateFormValue, - key: 'sandboxAccountCredentials', - value: settings.sandboxAccountCredentials, - } } >
- { ) }
-
+ ) } { ! settings.sandboxConnected && ( - { 'Connect a PayPal Sandbox account in order to test your website. Transactions made will not result in actual money movement. Do not fulfil orders completed in Sandbox mode.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_EMPTY, - callback: updateFormValue, - key: 'sandboxAccountCredentials', - value: settings.sandboxAccountCredentials, - } } - > -
- - updateFormValue( 'sandboxMode', newValue ) - } - label={ __( + options={ [ + { + id: 'sandbox_mode', + value: 'sandbox_mode', + label: __( 'Sandbox Mode', 'woocommerce-paypal-payments' - ) } - description={ __( + ), + description: __( 'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.', 'woocommerce-paypal-payments' - ) } - > - - - - updateFormValue( 'sandboxMode', newValue ) - } - label={ __( + ), + additionalContent: ( + + ), + }, + { + id: 'manual_connect', + value: 'manual_connect', + label: __( 'Manual Connect', 'woocommerce-paypal-payments' - ) } - description={ sprintf( - // translators: %s: Link to creating PayPal REST application + ), + description: sprintf( __( 'For advanced users: Connect a custom PayPal REST app for full control over your integration. For more information on creating a PayPal REST application, click here.', 'woocommerce-paypal-payments' ), '#' - ) } - > - - - - -
-
+ ), + additionalContent: ( + <> + + + + + ), + }, + ] } + actionProps={ { + name: 'paypal_connect_sandbox', + key: 'sandboxMode', + currentValue: settings.sandboxMode, + callback: updateFormValue, + } } + /> ) } -
+ ); }; + export default Sandbox; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/SavePaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/SavePaymentMethods.js index 8fdefbb9c..f10907c4c 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/SavePaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/SavePaymentMethods.js @@ -1,75 +1,83 @@ -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_EMPTY, - SETTINGS_BLOCK_TYPE_TOGGLE, -} from '../../../../ReusableComponents/SettingsBlock'; import { __, sprintf } from '@wordpress/i18n'; +import { + SettingsBlock, + ToggleSettingsBlock, + Title, + Description, +} from '../../../../ReusableComponents/SettingsBlocks'; +import { Header } from '../../../../ReusableComponents/SettingsBlocks/SettingsBlockElements'; const SavePaymentMethods = ( { updateFormValue, settings } ) => { return ( future payments[MISSING_LINK] and subscriptions[MISSING_LINK], simplifying checkout and enabling recurring transactions.', - 'woocommerce-paypal-payments' + components={ [ + () => ( + <> +
+ + { __( + 'Save payment methods', + 'woocommerce-paypal-payments' + ) } + + + { __( + 'Securely store customers’ payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.', + 'woocommerce-paypal-payments' + ) } + +
+ ), - '#', - '#' - ) } - type={ SETTINGS_BLOCK_STYLING_TYPE_PRIMARY } - style={ SETTINGS_BLOCK_STYLING_TYPE_PRIMARY } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_EMPTY, - } } - > - This will disable all Pay Later features and Alternative Payment Methods on your site.', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later', - 'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods' - ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } - value={ settings.savePaypalAndVenmo } - actionProps={ { - type: SETTINGS_BLOCK_TYPE_TOGGLE, - value: settings.savePaypalAndVenmo, - callback: updateFormValue, - key: 'savePaypalAndVenmo', - } } - /> - - + () => ( + This will disable all Pay Later features and Alternative Payment Methods on your site.', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later', + 'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods' + ), + } } + /> + } + actionProps={ { + value: settings.savePaypalAndVenmo, + callback: updateFormValue, + key: 'savePaypalAndVenmo', + } } + /> + ), + () => ( + + ), + ] } + /> ); }; + export default SavePaymentMethods; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting.js index fdc4ad28f..f53a360c7 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting.js @@ -1,65 +1,73 @@ -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_BUTTON, - SETTINGS_BLOCK_TYPE_EMPTY, - SETTINGS_BLOCK_TYPE_TOGGLE, - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, -} from '../../../../ReusableComponents/SettingsBlock'; import { __ } from '@wordpress/i18n'; +import { + Header, + Title, + Description, + AccordionSettingsBlock, + ToggleSettingsBlock, + ButtonSettingsBlock, +} from '../../../../ReusableComponents/SettingsBlocks'; +import SettingsBlock from '../../../../ReusableComponents/SettingsBlocks/SettingsBlock'; const Troubleshooting = ( { updateFormValue, settings } ) => { return ( - - - - + components={ [ + () => ( + <> +
+ + { __( + 'Subscribed PayPal webhooks', + 'woocommerce-paypal-payments' + ) } + + + { __( + 'The following PayPal webhooks are subscribed. More information about the webhooks is available in the', + 'woocommerce-paypal-payments' + ) }{ ' ' } + + { __( + 'Webhook Status documentation', + 'woocommerce-paypal-payments' + ) } + + . + +
+ + + ), + ] } + /> - { 'Click to remove the current webhook subscription and subscribe again, for example, if the website domain or URL structure changed.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } actionProps={ { - type: SETTINGS_BLOCK_TYPE_BUTTON, buttonType: 'secondary', callback: () => console.log( @@ -83,14 +89,13 @@ const Troubleshooting = ( { updateFormValue, settings } ) => { ), } } /> - console.log( @@ -103,7 +108,7 @@ const Troubleshooting = ( { updateFormValue, settings } ) => { ), } } /> - + ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/CommonSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/CommonSettings.js index 0066a5fcd..dad5da83e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/CommonSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/CommonSettings.js @@ -1,10 +1,8 @@ import { __ } from '@wordpress/i18n'; -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_INPUT, - SETTINGS_BLOCK_TYPE_TOGGLE, -} from '../../../ReusableComponents/SettingsBlock'; +import { + InputSettingsBlock, + ToggleSettingsBlock, +} from '../../../ReusableComponents/SettingsBlocks'; import SettingsCard from '../../../ReusableComponents/SettingsCard'; import OrderIntent from './Blocks/OrderIntent'; import SavePaymentMethods from './Blocks/SavePaymentMethods'; @@ -13,19 +11,21 @@ const CommonSettings = ( { updateFormValue, settings } ) => { return ( - { ), } } /> + + - { 'Let PayPal customers skip the Order Review page by selecting shipping options directly within PayPal.', 'woocommerce-paypal-payments' ) } - style={ SETTINGS_BLOCK_STYLING_TYPE_SECONDARY } actionProps={ { - type: SETTINGS_BLOCK_TYPE_TOGGLE, callback: updateFormValue, key: 'payNowExperience', value: settings.payNowExperience, diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ConnectionStatus.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ConnectionStatus.js new file mode 100644 index 000000000..b1018d44c --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ConnectionStatus.js @@ -0,0 +1,53 @@ +import { __ } from '@wordpress/i18n'; +import SettingsCard from '../../../ReusableComponents/SettingsCard'; +import ConnectionInfo, { + connectionStatusDataDefault, +} from '../../../ReusableComponents/ConnectionInfo'; +import TitleBadge, { + TITLE_BADGE_NEGATIVE, + TITLE_BADGE_POSITIVE, +} from '../../../ReusableComponents/TitleBadge'; +const ConnectionStatus = () => { + return ( + +
+
+
+ { connectionStatusDataDefault.connectionStatus ? ( + + ) : ( + + ) } +
+
+ { connectionStatusDataDefault.connectionStatus && ( + + ) } +
+
+ ); +}; +export default ConnectionStatus; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ExpertSettings.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ExpertSettings.js index 78e8bcd20..56a8e63c6 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ExpertSettings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/ExpertSettings.js @@ -1,11 +1,9 @@ import { __ } from '@wordpress/i18n'; -import SettingsBlock, { - SETTINGS_BLOCK_STYLING_TYPE_PRIMARY, - SETTINGS_BLOCK_STYLING_TYPE_SECONDARY, - SETTINGS_BLOCK_TYPE_SELECT, - SETTINGS_BLOCK_TYPE_TOGGLE_CONTENT, -} from '../../../ReusableComponents/SettingsBlock'; import SettingsCard from '../../../ReusableComponents/SettingsCard'; +import { + Content, + ContentWrapper, +} from '../../../ReusableComponents/SettingsBlocks'; import Sandbox from './Blocks/Sandbox'; import Troubleshooting from './Blocks/Troubleshooting'; import PaypalSettings from './Blocks/PaypalSettings'; @@ -25,25 +23,37 @@ const ExpertSettings = ( { updateFormValue, settings } ) => { callback: updateFormValue, key: 'payNowExperience', } } + contentContainer={ false } > - + + + + - + + + - - + + + + + + + +
); }; From 71461633014cf82e4d07d7ef9eed380afc50cf7a Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 12:04:02 +0100 Subject: [PATCH 59/93] =?UTF-8?q?=E2=9C=A8=20Re-implement=20conditional=20?= =?UTF-8?q?OXXO=20and=20PUI=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Overview/TabPaymentMethods.js | 106 ++++++++++-------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js index d233b9623..c1576da10 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPaymentMethods.js @@ -1,36 +1,34 @@ import { __ } from '@wordpress/i18n'; +import { useMemo } from '@wordpress/element'; + import SettingsCard from '../../ReusableComponents/SettingsCard'; import PaymentMethodsBlock from '../../ReusableComponents/SettingsBlocks/PaymentMethodsBlock'; +import { CommonHooks } from '../../../data'; import ModalPayPal from './Modals/ModalPayPal'; import ModalFastlane from './Modals/ModalFastlane'; import ModalAcdc from './Modals/ModalAcdc'; -import { CommonHooks } from '../../../data'; const TabPaymentMethods = () => { - const renderPaymentMethods = ( data ) => { - const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); - const conditionallyUpdatedPaymentMethods = [ - ...data, - ...( storeCountry === 'DE' && storeCurrency === 'EUR' - ? [ puiPaymentMethod ] - : [] ), - ...( storeCountry === 'MX' && storeCurrency === 'MXN' - ? [ oxxoPaymentMethod ] - : [] ), - ]; + const filteredPaymentMethods = useMemo( () => { + const contextProps = { storeCountry, storeCurrency }; - return ( -
- { conditionallyUpdatedPaymentMethods.map( ( paymentMethod ) => ( - - ) ) } -
- ); - }; + return { + payPalCheckout: filterPaymentMethods( + paymentMethodsPayPalCheckout, + contextProps + ), + onlineCardPayments: filterPaymentMethods( + paymentMethodsOnlineCardPayments, + contextProps + ), + alternative: filterPaymentMethods( + paymentMethodsAlternative, + contextProps + ), + }; + }, [ storeCountry, storeCurrency ] ); return (
@@ -44,7 +42,7 @@ const TabPaymentMethods = () => { contentContainer={ false } > { contentContainer={ false } > { contentContainer={ false } >
); }; -const paymentMethodsPayPalCheckoutDefault = [ +function filterPaymentMethods( paymentMethods, contextProps ) { + return paymentMethods.filter( ( method ) => + typeof method.condition === 'function' + ? method.condition( contextProps ) + : true + ); +} + +const paymentMethodsPayPalCheckout = [ { id: 'paypal', title: __( 'PayPal', 'woocommerce-paypal-payments' ), @@ -126,7 +132,7 @@ const paymentMethodsPayPalCheckoutDefault = [ }, ]; -const paymentMethodsOnlineCardPaymentsDefault = [ +const paymentMethodsOnlineCardPayments = [ { id: 'advanced_credit_and_debit_card_payments', title: __( @@ -170,7 +176,7 @@ const paymentMethodsOnlineCardPaymentsDefault = [ }, ]; -const paymentMethodsAlternativeDefault = [ +const paymentMethodsAlternative = [ { id: 'bancontact', title: __( 'Bancontact', 'woocommerce-paypal-payments' ), @@ -243,26 +249,28 @@ const paymentMethodsAlternativeDefault = [ ), icon: 'payment-method-multibanco', }, + { + id: 'pui', + title: __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ), + description: __( + 'Pay upon Invoice is an invoice payment method in Germany. It is a local buy now, pay later payment method that allows the buyer to place an order, receive the goods, try them, verify they are in good order, and then pay the invoice within 30 days.', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-ratepay', + condition: ( { storeCountry, storeCurrency } ) => + storeCountry === 'DE' && storeCurrency === 'EUR', + }, + { + id: 'oxxo', + title: __( 'OXXO', 'woocommerce-paypal-payments' ), + description: __( + 'OXXO is a Mexican chain of convenience stores. *Get PayPal account permission to use OXXO payment functionality by contacting us at (+52) 800–925–0304', + 'woocommerce-paypal-payments' + ), + icon: 'payment-method-oxxo', + condition: ( { storeCountry, storeCurrency } ) => + storeCountry === 'MX' && storeCurrency === 'MXN', + }, ]; -const puiPaymentMethod = { - id: 'pui', - title: __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ), - description: __( - 'Pay upon Invoice is an invoice payment method in Germany. It is a local buy now, pay later payment method that allows the buyer to place an order, receive the goods, try them, verify they are in good order, and then pay the invoice within 30 days.', - 'woocommerce-paypal-payments' - ), - icon: 'payment-method-ratepay', -}; - -const oxxoPaymentMethod = { - id: 'oxxo', - title: __( 'OXXO', 'woocommerce-paypal-payments' ), - description: __( - 'OXXO is a Mexican chain of convenience stores. *Get PayPal account permission to use OXXO payment functionality by contacting us at (+52) 800–925–0304', - 'woocommerce-paypal-payments' - ), - icon: 'payment-method-oxxo', -}; - export default TabPaymentMethods; From 1b557f1619e98d801be65b3033c71607be6b414e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 14:03:07 +0100 Subject: [PATCH 60/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20generic=20Acc?= =?UTF-8?q?ordion=20more=20generic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_accordion-section.scss | 25 ++--- .../ReusableComponents/AccordionSection.js | 99 ++++++++++--------- .../resources/js/hooks/useAccordionState.js | 39 ++++++++ 3 files changed, 105 insertions(+), 58 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/hooks/useAccordionState.js diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss index 28ac713ce..d4894abd8 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_accordion-section.scss @@ -2,26 +2,27 @@ margin-left: auto; margin-right: auto; - &--title { + &__toggler { + display: block; + cursor: pointer; + + background: transparent; + border: 0; + box-shadow: none; + padding: 0; + margin: 24px auto; + } + + &__title-wrapper { @include font(14, 32, 450); color: $color-gray-900; display: flex; align-items: center; gap: 16px; - margin: 24px auto; - border: 0; - background: transparent; - cursor: pointer; } - &--content { + &__content { margin: 24px 0 0; } - - &.ppcp--is-open { - .ppcp-r-accordion--icon { - transform: rotate(180deg); - } - } } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js index 23f01a09c..f5b071945 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js @@ -1,65 +1,72 @@ -import { useEffect } from '@wordpress/element'; import { Icon } from '@wordpress/components'; import { chevronDown, chevronUp } from '@wordpress/icons'; -import { useState } from 'react'; +import classNames from 'classnames'; + +import { useAccordionState } from '../../hooks/useAccordionState'; + +// Provide defaults for all layout components so the generic version just works. +const DefaultHeader = ( { children, className = '' } ) => ( +
+ { children } +
+); +const DefaultTitleWrapper = ( { children } ) => ( +
{ children }
+); +const DefaultTitle = ( { children } ) => ( + { children } +); +const DefaultAction = ( { children } ) => ( + { children } +); +const DefaultDescription = ( { children } ) => ( +
{ children }
+); const Accordion = ( { title, + id = '', initiallyOpen = null, + description = '', + children = null, className = '', - id = '', - children, -} ) => { - const determineInitialState = () => { - if ( id && initiallyOpen === null ) { - return window.location.hash === `#${ id }`; - } - return !! initiallyOpen; - }; - - const [ isOpen, setIsOpen ] = useState( determineInitialState ); - useEffect( () => { - const handleHashChange = () => { - if ( id && window.location.hash === `#${ id }` ) { - setIsOpen( true ); - } - }; - - window.addEventListener( 'hashchange', handleHashChange ); - - return () => { - window.removeEventListener( 'hashchange', handleHashChange ); - }; - }, [ id ] ); - - const toggleOpen = ( ev ) => { - setIsOpen( ! isOpen ); - ev?.preventDefault(); - return false; - }; + // Layout components can be overridden by the caller + Header = DefaultHeader, + TitleWrapper = DefaultTitleWrapper, + Title = DefaultTitle, + Action = DefaultAction, + Description = DefaultDescription, +} ) => { + const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } ); + const wrapperClasses = classNames( 'ppcp-r-accordion', className, { + 'ppcp--is-open': isOpen, + } ); - const wrapperClasses = [ 'ppcp-r-accordion' ]; - if ( className ) { - wrapperClasses.push( className ); - } - if ( isOpen ) { - wrapperClasses.push( 'ppcp--is-open' ); - } + const icon = isOpen ? chevronUp : chevronDown; return ( -
+
- { isOpen && ( -
{ children }
+ { isOpen && children && ( +
{ children }
) }
); diff --git a/modules/ppcp-settings/resources/js/hooks/useAccordionState.js b/modules/ppcp-settings/resources/js/hooks/useAccordionState.js new file mode 100644 index 000000000..f54018262 --- /dev/null +++ b/modules/ppcp-settings/resources/js/hooks/useAccordionState.js @@ -0,0 +1,39 @@ +import { useEffect, useState } from '@wordpress/element'; + +const checkIfCurrentTab = ( id ) => { + return id && window.location.hash === `#${ id }`; +}; + +const determineInitialState = ( id, initiallyOpen ) => { + if ( initiallyOpen !== null ) { + return initiallyOpen; + } + return checkIfCurrentTab( id ); +}; + +export function useAccordionState( { id = '', initiallyOpen = null } ) { + const [ isOpen, setIsOpen ] = useState( + determineInitialState( id, initiallyOpen ) + ); + + useEffect( () => { + const handleHashChange = () => { + if ( checkIfCurrentTab( id ) ) { + setIsOpen( true ); + } + }; + + window.addEventListener( 'hashchange', handleHashChange ); + return () => { + window.removeEventListener( 'hashchange', handleHashChange ); + }; + }, [ id ] ); + + const toggleOpen = ( ev ) => { + setIsOpen( ! isOpen ); + ev?.preventDefault(); + return false; + }; + + return { isOpen, toggleOpen }; +} From 2a9766fc76e4d2221a3a99f3b130707e5b941fc6 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 14:43:27 +0100 Subject: [PATCH 61/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reuse=20the=20generi?= =?UTF-8?q?c=20Accordion=20for=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../css/components/screens/_settings.scss | 29 +------- .../screens/settings/_block-accordion.scss | 38 ++++++++++ .../SettingsBlocks/AccordionSettingsBlock.js | 69 +++++++------------ 3 files changed, 63 insertions(+), 73 deletions(-) create mode 100644 modules/ppcp-settings/resources/css/components/screens/settings/_block-accordion.scss diff --git a/modules/ppcp-settings/resources/css/components/screens/_settings.scss b/modules/ppcp-settings/resources/css/components/screens/_settings.scss index 8cf4fe593..b98a5f7e1 100644 --- a/modules/ppcp-settings/resources/css/components/screens/_settings.scss +++ b/modules/ppcp-settings/resources/css/components/screens/_settings.scss @@ -1,3 +1,5 @@ +@import "./settings/block-accordion"; + // Container and Tab Settings .ppcp-r-tabs.settings, .ppcp-r-container--settings { @@ -275,7 +277,6 @@ align-items: center; } - &.ppcp-r-settings-block__accordion, &.ppcp-r-settings-block__feature { .ppcp-r-settings-block__title { @include font(13, 20, 600); @@ -283,11 +284,6 @@ text-transform: none; } - .ppcp-r-settings-block--accordion__title { - @include font(14, 20, 600); - } - - .ppcp-r-settings-block--accordion__description, .ppcp-r-settings-block__feature__description { color: $color-gray-700; @include font(13, 20, 400); @@ -524,27 +520,6 @@ } } -.ppcp-r-settings-block__accordion { - .ppcp-r-settings-block--accordion__header { - gap: 4px; - } - - &.ppcp-r-settings-block--content-visible .ppcp-r-settings-block--accordion__header { - margin-bottom: 24px; - } - - &.ppcp-r-settings-block { - gap: 0; - .ppcp-r-settings-block:not(:last-child) { - &:not(.ppcp-r__radio-content-additional .ppcp-r-settings-block) { - padding-bottom: 32px; - margin-bottom: 32px; - border-bottom: 1px solid $color-divider; - } - } - } -} - .ppcp-r-settings-block--toggle-content { .ppcp-r-settings-block__content { margin-top: 32px; diff --git a/modules/ppcp-settings/resources/css/components/screens/settings/_block-accordion.scss b/modules/ppcp-settings/resources/css/components/screens/settings/_block-accordion.scss new file mode 100644 index 000000000..c77a3eb91 --- /dev/null +++ b/modules/ppcp-settings/resources/css/components/screens/settings/_block-accordion.scss @@ -0,0 +1,38 @@ +.ppcp-r-settings-block__accordion { + > .ppcp-r-accordion { + width: 100%; + + .ppcp-r-accordion__toggler { + width: 100%; + margin: 0; + text-align: unset; + } + } + + &.ppcp-r-settings-block { + gap: 0; + + .ppcp-r-settings-block__title { + @include font(13, 20, 600); + color: $color-text-text; + text-transform: none; + } + + .ppcp-r-settings-block--accordion__title { + @include font(14, 20, 600); + } + + .ppcp-r-settings-block--accordion__description { + color: $color-gray-700; + @include font(13, 20, 400); + } + + .ppcp-r-settings-block:not(:last-child) { + &:not(.ppcp-r__radio-content-additional .ppcp-r-settings-block) { + padding-bottom: 32px; + margin-bottom: 32px; + border-bottom: 1px solid $color-divider; + } + } + } +} diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js index a39585ca9..8a953c805 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js @@ -1,5 +1,4 @@ -import { useState } from '@wordpress/element'; -import data from '../../../utils/data'; +import Accordion from '../AccordionSection'; import SettingsBlock from './SettingsBlock'; import { Header, @@ -9,48 +8,26 @@ import { TitleWrapper, } from './SettingsBlockElements'; -const AccordionSettingsBlock = ( { title, description, ...props } ) => { - const [ isVisible, setIsVisible ] = useState( false ); +const SettingsAccordion = ( { title, description, children, ...props } ) => ( + ( + + { children } + + ), + ] } + /> +); - return ( - ( - <> -
- - - { title } - - -
- setIsVisible( ! isVisible ) - } - > - { data().getImage( - 'icon-arrow-down.svg' - ) } -
-
-
- - { description } - -
- { isVisible && props.children && ( - <>{ props.children } - ) } - - ), - ] } - /> - ); -}; - -export default AccordionSettingsBlock; +export default SettingsAccordion; From 53a4eabe8b2ce6b8827530f9ef96b47f73dc1098 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 9 Dec 2024 15:16:39 +0100 Subject: [PATCH 62/93] Fix phpcs --- modules/ppcp-settings/src/Data/OnboardingProfile.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index 633f13269..a2d8e6c36 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -42,6 +42,7 @@ class OnboardingProfile extends AbstractDataModel { * @param bool $can_use_casual_selling Whether casual selling is enabled in the store's country. * @param bool $can_use_vaulting Whether vaulting is enabled in the store's country. * @param bool $can_use_card_payments Whether credit card payments are possible. + * @param bool $can_use_subscriptions Whether WC Subscriptions plugin is active. * * @throws RuntimeException If the OPTION_KEY is not defined in the child class. */ @@ -56,7 +57,7 @@ public function __construct( $this->flags['can_use_casual_selling'] = $can_use_casual_selling; $this->flags['can_use_vaulting'] = $can_use_vaulting; $this->flags['can_use_card_payments'] = $can_use_card_payments; - $this->flags['can_use_subscriptions'] = $can_use_subscriptions; + $this->flags['can_use_subscriptions'] = $can_use_subscriptions; } /** From a0d610669cb12777e3a23b23a3c710c5ce3c747f Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 9 Dec 2024 17:38:17 +0100 Subject: [PATCH 63/93] Hide price notice when country not available --- .../WelcomeDocs/WelcomeDocs.js | 26 ++++++++---------- .../WelcomeDocs/pricesBasedDescription.js | 10 +++++++ .../Screens/Onboarding/StepPaymentMethods.js | 27 ++++++++----------- 3 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js index b3b60fee1..eabb5b3db 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js @@ -1,7 +1,8 @@ -import { __, sprintf } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import AcdcFlow from './AcdcFlow'; import BcdcFlow from './BcdcFlow'; -import { Button } from '@wordpress/components'; +import { countryPriceInfo } from '../../../utils/countryPriceInfo'; +import { pricesBasedDescription } from './pricesBasedDescription'; const WelcomeDocs = ( { useAcdc, @@ -10,15 +11,6 @@ const WelcomeDocs = ( { storeCountry, storeCurrency, } ) => { - const pricesBasedDescription = sprintf( - // translators: %s: Link to PayPal REST application guide - __( - '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ); - return (

@@ -41,10 +33,14 @@ const WelcomeDocs = ( { storeCurrency={ storeCurrency } /> ) } -

+ { storeCountry in countryPriceInfo && ( +

+ ) }

); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js new file mode 100644 index 000000000..c4d3eb983 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js @@ -0,0 +1,10 @@ +import { __, sprintf } from '@wordpress/i18n'; + +export const pricesBasedDescription = sprintf( + // translators: %s: Link to PayPal REST application guide + __( + '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' +); diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js index 83ca5540f..e94f176f7 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js @@ -1,10 +1,12 @@ -import { __, sprintf } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import OnboardingHeader from '../../ReusableComponents/OnboardingHeader'; import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; import SelectBox from '../../ReusableComponents/SelectBox'; import { CommonHooks, OnboardingHooks } from '../../../data'; import OptionalPaymentMethods from '../../ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods'; +import { pricesBasedDescription } from '../../ReusableComponents/WelcomeDocs/pricesBasedDescription'; +import { countryPriceInfo } from '../../../utils/countryPriceInfo'; const OPM_RADIO_GROUP_NAME = 'optional-payment-methods'; @@ -16,15 +18,6 @@ const StepPaymentMethods = ( {} ) => { const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); - const pricesBasedDescription = sprintf( - // translators: %s: Link to PayPal REST application guide - __( - '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' - ); - return (
{ type="radio" > -

+ { storeCountry in countryPriceInfo && ( +

+ ) }
); From 1826b95c08afcbf7f07a381ca029a90167a6eb7c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 18:16:13 +0100 Subject: [PATCH 64/93] =?UTF-8?q?=E2=9C=A8=20Introduce=20new=20BusyStateWr?= =?UTF-8?q?apper=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reusable-components/_busy-state.scss | 10 ++ .../_settings-toggle-block.scss | 6 - .../ppcp-settings/resources/css/style.scss | 5 +- .../ReusableComponents/BusyStateWrapper.js | 57 ++++++++ .../ReusableComponents/SettingsToggleBlock.js | 13 +- .../Components/AdvancedOptionsForm.js | 131 ++++++++++-------- .../Onboarding/Components/ConnectionButton.js | 22 +-- .../Onboarding/Components/Navigation.js | 12 +- .../Screens/Onboarding/StepWelcome.js | 23 +-- 9 files changed, 176 insertions(+), 103 deletions(-) create mode 100644 modules/ppcp-settings/resources/css/components/reusable-components/_busy-state.scss create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_busy-state.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_busy-state.scss new file mode 100644 index 000000000..4254320aa --- /dev/null +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_busy-state.scss @@ -0,0 +1,10 @@ +.ppcp-r-busy-wrapper { + position: relative; + + &.ppcp--is-loading { + pointer-events: none; + user-select: none; + + --spinner-overlay-color: #fff4; + } +} diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-toggle-block.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-toggle-block.scss index e5157a862..af4d264ad 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_settings-toggle-block.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_settings-toggle-block.scss @@ -31,10 +31,4 @@ &__toggled-content { margin-top: 24px; } - - &.ppcp--is-loading { - pointer-events: none; - - --spinner-overlay-color: #fff4; - } } diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index 7ad7aa7a4..70e9b8971 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -3,10 +3,11 @@ #ppcp-settings-container { @import './global'; - @import './components/reusable-components/onboarding-header'; + @import './components/reusable-components/busy-state'; @import './components/reusable-components/button'; - @import './components/reusable-components/settings-toggle-block'; @import './components/reusable-components/separator'; + @import './components/reusable-components/onboarding-header'; + @import './components/reusable-components/settings-toggle-block'; @import './components/reusable-components/payment-method-icons'; @import "./components/reusable-components/payment-method-item"; @import './components/reusable-components/settings-wrapper'; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js new file mode 100644 index 000000000..79799bce2 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js @@ -0,0 +1,57 @@ +import { + Children, + isValidElement, + cloneElement, + useMemo, +} from '@wordpress/element'; +import classNames from 'classnames'; + +import { CommonHooks } from '../../data'; +import SpinnerOverlay from './SpinnerOverlay'; + +/** + * Wraps interactive child elements and modifies their behavior based on the global `isBusy` state. + * Allows custom processing of child props via the `onBusy` callback. + * + * @param {Object} props - Component properties. + * @param {Children} props.children - Child components to wrap. + * @param {boolean} props.enabled - Enables or disables the busy-state logic. + * @param {string} props.className - Additional class names for the wrapper. + * @param {Function} props.onBusy - Callback to process child props when busy. + */ +const BusyStateWrapper = ( { + children, + enabled = true, + className = '', + onBusy = () => ( { disabled: true } ), +} ) => { + const { isBusy } = CommonHooks.useBusyState(); + + const markAsBusy = isBusy && enabled; + + const wrapperClassName = classNames( 'ppcp-r-busy-wrapper', className, { + 'ppcp--is-loading': markAsBusy, + } ); + + const memoizedChildren = useMemo( + () => + Children.map( children, ( child ) => + isValidElement( child ) + ? cloneElement( + child, + markAsBusy ? onBusy( child.props ) : {} + ) + : child + ), + [ children, markAsBusy, onBusy ] + ); + + return ( +
+ { markAsBusy && } + { memoizedChildren } +
+ ); +}; + +export default BusyStateWrapper; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js index d8dda1cfb..4a7cf1a20 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsToggleBlock.js @@ -1,23 +1,17 @@ import { ToggleControl } from '@wordpress/components'; import { useRef } from '@wordpress/element'; -import SpinnerOverlay from './SpinnerOverlay'; - const SettingsToggleBlock = ( { isToggled, setToggled, - isLoading = false, + disabled = false, ...props } ) => { const toggleRef = useRef( null ); const blockClasses = [ 'ppcp-r-toggle-block' ]; - if ( isLoading ) { - blockClasses.push( 'ppcp--is-loading' ); - } - const handleLabelClick = () => { - if ( ! toggleRef.current || isLoading ) { + if ( ! toggleRef.current || disabled ) { return; } @@ -52,13 +46,12 @@ const SettingsToggleBlock = ( { ref={ toggleRef } checked={ isToggled } onChange={ ( newState ) => setToggled( newState ) } - disabled={ isLoading } + disabled={ disabled } />
{ props.children && isToggled && (
- { isLoading && } { props.children }
) } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index bb2d58209..9b29815f7 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -20,6 +20,7 @@ import { } from '../../../../hooks/useHandleConnections'; import ConnectionButton from './ConnectionButton'; +import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper'; const FORM_ERRORS = { noClientId: __( @@ -89,7 +90,7 @@ const AdvancedOptionsForm = () => { handleConnectViaIdAndSecret( { validation: validateManualConnectionForm, } ), - [ validateManualConnectionForm ] + [ handleConnectViaIdAndSecret, validateManualConnectionForm ] ); useEffect( () => { @@ -124,69 +125,79 @@ const AdvancedOptionsForm = () => { return ( <> - - + - + isToggled={ !! isSandboxMode } + setToggled={ setSandboxMode } + > + + + - ( { + disabled: true, + label: props.label + ' ...', + } ) } > - - { clientValid || ( -

- { FORM_ERRORS.invalidClientId } -

- ) } - - -
+ + + { clientValid || ( +

+ { FORM_ERRORS.invalidClientId } +

+ ) } + + +
+ ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js index 0a0ac5bfb..284943e18 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/ConnectionButton.js @@ -8,6 +8,7 @@ import { useProductionConnection, useSandboxConnection, } from '../../../../hooks/useHandleConnections'; +import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper'; const ConnectionButton = ( { title, @@ -15,13 +16,11 @@ const ConnectionButton = ( { variant = 'primary', showIcon = true, } ) => { - const { isBusy } = CommonHooks.useBusyState(); const { handleSandboxConnect } = useSandboxConnection(); const { handleProductionConnect } = useProductionConnection(); const className = classNames( 'ppcp-r-connection-button', { 'sandbox-mode': isSandbox, 'live-mode': ! isSandbox, - 'ppcp--is-loading': isBusy, } ); const handleConnectClick = async () => { @@ -33,15 +32,16 @@ const ConnectionButton = ( { }; return ( - + + + ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index 82bfdb656..a30cb4ce2 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -6,6 +6,7 @@ import classNames from 'classnames'; import { OnboardingHooks } from '../../../../data'; import useIsScrolled from '../../../../hooks/useIsScrolled'; +import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper'; const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => { const { title, isFirst, percentage, showNext, canProceed } = stepDetails; @@ -20,7 +21,10 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => { return (
-
+ -
+ { ! isFirst && NextButton( { showNext, isDisabled, onNext, onExit } ) } @@ -42,7 +46,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => { const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => { return ( -
+ @@ -55,7 +59,7 @@ const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => { { __( 'Continue', 'woocommerce-paypal-payments' ) } ) } -
+ ); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js index 461d95d26..f8abf9ea5 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -9,6 +9,7 @@ import AccordionSection from '../../ReusableComponents/AccordionSection'; import AdvancedOptionsForm from './Components/AdvancedOptionsForm'; import { CommonHooks } from '../../../data'; +import BusyStateWrapper from '../../ReusableComponents/BusyStateWrapper'; const StepWelcome = ( { setStep, currentStep } ) => { const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); @@ -34,16 +35,18 @@ const StepWelcome = ( { setStep, currentStep } ) => { 'woocommerce-paypal-payments' ) }

- + + +
Date: Mon, 9 Dec 2024 18:48:56 +0100 Subject: [PATCH 65/93] =?UTF-8?q?=F0=9F=92=84=20Fix=20multiple=20spinners?= =?UTF-8?q?=20in=20nested=20Busy-wrappers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reusable-components/_spinner-overlay.scss | 1 + .../ReusableComponents/BusyStateWrapper.js | 37 ++++++++++++------- .../Onboarding/Components/Navigation.js | 6 ++- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_spinner-overlay.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_spinner-overlay.scss index 2f32b118f..8f5e136e9 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_spinner-overlay.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_spinner-overlay.scss @@ -12,5 +12,6 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); + margin: 0; } } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js index 79799bce2..959b71bfe 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BusyStateWrapper.js @@ -3,34 +3,43 @@ import { isValidElement, cloneElement, useMemo, + createContext, + useContext, } from '@wordpress/element'; import classNames from 'classnames'; import { CommonHooks } from '../../data'; import SpinnerOverlay from './SpinnerOverlay'; +// Create context to track the busy state across nested wrappers +const BusyContext = createContext( false ); + /** * Wraps interactive child elements and modifies their behavior based on the global `isBusy` state. * Allows custom processing of child props via the `onBusy` callback. * - * @param {Object} props - Component properties. - * @param {Children} props.children - Child components to wrap. - * @param {boolean} props.enabled - Enables or disables the busy-state logic. - * @param {string} props.className - Additional class names for the wrapper. - * @param {Function} props.onBusy - Callback to process child props when busy. + * @param {Object} props - Component properties. + * @param {Children} props.children - Child components to wrap. + * @param {boolean} props.enabled - Enables or disables the busy-state logic. + * @param {boolean} props.busySpinner - Allows disabling the spinner in busy-state. + * @param {string} props.className - Additional class names for the wrapper. + * @param {Function} props.onBusy - Callback to process child props when busy. */ const BusyStateWrapper = ( { children, enabled = true, + busySpinner = true, className = '', onBusy = () => ( { disabled: true } ), } ) => { const { isBusy } = CommonHooks.useBusyState(); + const hasBusyParent = useContext( BusyContext ); - const markAsBusy = isBusy && enabled; + const isBusyComponent = isBusy && enabled; + const showSpinner = busySpinner && isBusyComponent && ! hasBusyParent; const wrapperClassName = classNames( 'ppcp-r-busy-wrapper', className, { - 'ppcp--is-loading': markAsBusy, + 'ppcp--is-loading': isBusyComponent, } ); const memoizedChildren = useMemo( @@ -39,18 +48,20 @@ const BusyStateWrapper = ( { isValidElement( child ) ? cloneElement( child, - markAsBusy ? onBusy( child.props ) : {} + isBusyComponent ? onBusy( child.props ) : {} ) : child ), - [ children, markAsBusy, onBusy ] + [ children, isBusyComponent, onBusy ] ); return ( -
- { markAsBusy && } - { memoizedChildren } -
+ +
+ { showSpinner && } + { memoizedChildren } +
+
); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js index a30cb4ce2..3c12e1206 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/Navigation.js @@ -23,6 +23,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
From 6586cc06a1b938bb4a625f85b7a0821d8900d2bb Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 19:00:26 +0100 Subject: [PATCH 66/93] =?UTF-8?q?=E2=9C=A8=20Disable=20the=20browser=20war?= =?UTF-8?q?ning=20on=20page=20reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/Components/Screens/Settings.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js index e0634343c..faf65c047 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js @@ -1,3 +1,5 @@ +import { useEffect } from '@wordpress/element'; + import { OnboardingHooks } from '../../data'; import Onboarding from './Onboarding/Onboarding'; import SettingsScreen from './SettingsScreen'; @@ -5,6 +7,20 @@ import SettingsScreen from './SettingsScreen'; const Settings = () => { const onboardingProgress = OnboardingHooks.useSteps(); + // Disable the "Changes you made might not be saved" browser warning. + useEffect( () => { + const suppressBeforeUnload = ( event ) => { + event.stopImmediatePropagation(); + return undefined; + }; + + window.addEventListener( 'beforeunload', suppressBeforeUnload ); + + return () => { + window.removeEventListener( 'beforeunload', suppressBeforeUnload ); + }; + }, [] ); + if ( ! onboardingProgress.isReady ) { // TODO: Use better loading state indicator. return
Loading...
; From 90068aa461959ad125827ce674c4217afa6fc98e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 19:03:26 +0100 Subject: [PATCH 67/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Only=20disable=20con?= =?UTF-8?q?firmation=20logic=20for=20onboarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Screens/Onboarding/Onboarding.js | 15 +++++++++++++++ .../resources/js/Components/Screens/Settings.js | 16 ---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index e59bcdeeb..4d4a97fd3 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -3,6 +3,7 @@ import { OnboardingHooks } from '../../../data'; import { getSteps, getCurrentStep } from './availableSteps'; import Navigation from './Components/Navigation'; +import { useEffect } from '@wordpress/element'; const Onboarding = () => { const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); @@ -10,6 +11,20 @@ const Onboarding = () => { const Steps = getSteps( flags ); const currentStep = getCurrentStep( step, Steps ); + // Disable the "Changes you made might not be saved" browser warning. + useEffect( () => { + const suppressBeforeUnload = ( event ) => { + event.stopImmediatePropagation(); + return undefined; + }; + + window.addEventListener( 'beforeunload', suppressBeforeUnload ); + + return () => { + window.removeEventListener( 'beforeunload', suppressBeforeUnload ); + }; + }, [] ); + const handleNext = () => setStep( currentStep.nextStep ); const handlePrev = () => setStep( currentStep.prevStep ); const handleExit = () => { diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js index faf65c047..e0634343c 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js @@ -1,5 +1,3 @@ -import { useEffect } from '@wordpress/element'; - import { OnboardingHooks } from '../../data'; import Onboarding from './Onboarding/Onboarding'; import SettingsScreen from './SettingsScreen'; @@ -7,20 +5,6 @@ import SettingsScreen from './SettingsScreen'; const Settings = () => { const onboardingProgress = OnboardingHooks.useSteps(); - // Disable the "Changes you made might not be saved" browser warning. - useEffect( () => { - const suppressBeforeUnload = ( event ) => { - event.stopImmediatePropagation(); - return undefined; - }; - - window.addEventListener( 'beforeunload', suppressBeforeUnload ); - - return () => { - window.removeEventListener( 'beforeunload', suppressBeforeUnload ); - }; - }, [] ); - if ( ! onboardingProgress.isReady ) { // TODO: Use better loading state indicator. return
Loading...
; From 36de426260b703b9c005ca9e64971109c90fedab Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 19:24:34 +0100 Subject: [PATCH 68/93] =?UTF-8?q?=F0=9F=92=84=20Add=20a=20real=20loading?= =?UTF-8?q?=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/css/components/_app.scss | 22 +++++++++++++ .../ppcp-settings/resources/css/style.scss | 1 + .../ReusableComponents/SpinnerOverlay.js | 7 +++- .../js/Components/Screens/Settings.js | 33 ++++++++++++++----- 4 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 modules/ppcp-settings/resources/css/components/_app.scss diff --git a/modules/ppcp-settings/resources/css/components/_app.scss b/modules/ppcp-settings/resources/css/components/_app.scss new file mode 100644 index 000000000..7e69cbada --- /dev/null +++ b/modules/ppcp-settings/resources/css/components/_app.scss @@ -0,0 +1,22 @@ +/** + * Global app-level styles + */ + +.ppcp-r-app.loading { + height: 400px; + width: 400px; + position: absolute; + left: 50%; + transform: translate(-50%, 0); + text-align: center; + + .ppcp-r-spinner-overlay { + display: flex; + flex-direction: column; + justify-content: center; + } + + .ppcp-r-spinner-overlay__message { + transform: translate(0, 32px) + } +} diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index 70e9b8971..56fe55a62 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -23,6 +23,7 @@ @import './components/screens/onboarding'; @import './components/screens/settings'; @import './components/screens/overview/tab-styling'; + @import './components/app'; } @import './components/reusable-components/payment-method-modal'; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SpinnerOverlay.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SpinnerOverlay.js index dec732a3e..b4165b5ba 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SpinnerOverlay.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SpinnerOverlay.js @@ -1,8 +1,13 @@ import { Spinner } from '@wordpress/components'; -const SpinnerOverlay = () => { +const SpinnerOverlay = ( { message = '' } ) => { return (
+ { message && ( + + { message } + + ) }
); diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js index e0634343c..009d1d46b 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js @@ -1,20 +1,37 @@ +import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import classNames from 'classnames'; + import { OnboardingHooks } from '../../data'; +import SpinnerOverlay from '../ReusableComponents/SpinnerOverlay'; + import Onboarding from './Onboarding/Onboarding'; import SettingsScreen from './SettingsScreen'; const Settings = () => { const onboardingProgress = OnboardingHooks.useSteps(); - if ( ! onboardingProgress.isReady ) { - // TODO: Use better loading state indicator. - return
Loading...
; - } + const wrapperClass = classNames( 'ppcp-r-app', { + loading: ! onboardingProgress.isReady, + } ); + + const Content = useMemo( () => { + if ( ! onboardingProgress.isReady ) { + return ( + + ); + } + + if ( ! onboardingProgress.completed ) { + return ; + } - if ( ! onboardingProgress.completed ) { - return ; - } + return ; + }, [ onboardingProgress ] ); - return ; + return
{ Content }
; }; export default Settings; From 3d49241c5ec81aeb2e042b96d8aaccefeb62f982 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 10 Dec 2024 13:58:00 +0100 Subject: [PATCH 69/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Major=20button-styli?= =?UTF-8?q?ng=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/css/_variables.scss | 17 +++ .../reusable-components/_button.scss | 108 +++++++++++++----- .../screens/onboarding/_step-welcome.scss | 6 - .../Components/AdvancedOptionsForm.js | 7 +- .../Onboarding/Components/ConnectionButton.js | 5 +- 5 files changed, 107 insertions(+), 36 deletions(-) diff --git a/modules/ppcp-settings/resources/css/_variables.scss b/modules/ppcp-settings/resources/css/_variables.scss index 73b656f3a..10f427ea9 100644 --- a/modules/ppcp-settings/resources/css/_variables.scss +++ b/modules/ppcp-settings/resources/css/_variables.scss @@ -10,6 +10,7 @@ $color-gray-500: #BBBBBB; $color-gray-400: #CCCCCC; $color-gray-300: #EBEBEB; $color-gray-200: #E0E0E0; +$color-gray-100: #F0F0F0; $color-gray: #646970; $color-text-tertiary: #505050; $color-text-text: #070707; @@ -27,6 +28,8 @@ $max-width-settings: 938px; $card-vertical-gap: 48px; +/* define custom theming options */ + :root { --ppcp-color-app-bg: #{$color-white}; } @@ -37,4 +40,18 @@ $card-vertical-gap: 48px; --max-width-onboarding-content: #{$max-width-onboarding-content}; --max-container-width: var(--max-width-settings); + + --color-black: #{$color-black}; + --color-white: #{$color-white}; + --color-blueberry: #{$color-blueberry}; + --color-gray-900: #{$color-gray-900}; + --color-gray-800: #{$color-gray-800}; + --color-gray-700: #{$color-gray-700}; + --color-gray-600: #{$color-gray-600}; + --color-gray-500: #{$color-gray-500}; + --color-gray-400: #{$color-gray-400}; + --color-gray-300: #{$color-gray-300}; + --color-gray-200: #{$color-gray-200}; + --color-gray-100: #{$color-gray-100}; + --color-gradient-dark: #{$color-gradient-dark}; } diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss index 4174e6a23..558ccaaf2 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_button.scss @@ -1,48 +1,102 @@ +%button-style-default { + background-color: var(--button-background); + color: var(--button-color); + box-shadow: inset 0 0 0 1px var(--button-border-color); +} + +%button-style-hover { + background-color: var(--button-hover-background); + color: var(--button-hover-color); + box-shadow: inset 0 0 0 1px var(--button-hover-border-color); +} + +%button-style-disabled { + background-color: var(--button-disabled-background); + color: var(--button-disabled-color); + box-shadow: inset 0 0 0 1px var(--button-disabled-border-color); +} + +%button-shape-pill { + border-radius: 50px; + padding: 15px 32px; + height: auto; +} + button.components-button, a.components-button { - &.is-primary, &.is-secondary { - &:not(:disabled) { - background-color: $color-black; - } + /* default theme */ + --button-color: var(--color-gray-900); + --button-background: transparent; + --button-border-color: transparent; - &:disabled { - color: $color-gray-700; - } + --button-hover-color: var(--button-color); + --button-hover-background: var(--button-background); + --button-hover-border-color: var(--button-border-color); + + --button-disabled-color: var(--color-gray-500); + --button-disabled-background: transparent; + --button-disabled-border-color: transparent; + + /* style the button template */ - border-radius: 50px; - padding: 15px 32px; - height: auto; + &:not(:disabled) { + @extend %button-style-default; + } + + &:hover { + @extend %button-style-hover; + } + + &:disabled { + @extend %button-style-disabled; + } + + /* + ---------------------------------------------- + Customize variants using the theming variables + */ + + &.is-primary, + &.is-secondary { + @extend %button-shape-pill; } &.is-primary { @include font(14, 18, 900); - &:not(:disabled) { - background-color: $color-blueberry; - color: $color-white; - } + --button-color: #{$color-white}; + --button-background: #{$color-blueberry}; + + --button-disabled-color: #{$color-gray-100}; + --button-disabled-background: #{$color-gray-500}; } - &.is-secondary:not(:disabled) { - border-color: $color-blueberry; - background-color: $color-white; - color: $color-blueberry; + &.is-secondary { + --button-color: #{$color-blueberry}; + --button-background: #{$color-white}; + --button-border-color: #{$color-blueberry}; - &:hover { - background-color: $color-white; - background: none; - } + --button-disabled-color: #{$color-gray-600}; + --button-disabled-background: #{$color-gray-100}; + --button-disabled-border-color: #{$color-gray-400}; } &.is-tertiary { - color: $color-blueberry; - - &:hover { - color: $color-gradient-dark; - } + --button-color: #{$color-blueberry}; + --button-hover-color: #{$color-gradient-dark}; &:focus:not(:disabled) { border: none; box-shadow: none; } } + + &.small-button { + @include small-button; + } +} + +.ppcp--is-loading { + button.components-button, a.components-button { + @extend %button-style-disabled; + } } diff --git a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss index 47af9c99a..450251b6f 100644 --- a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss +++ b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-welcome.scss @@ -20,12 +20,6 @@ margin: 0 0 24px 0; } - .ppcp-r-toggle-block__toggled-content > button{ - @include small-button; - color: $color-white; - border: none; - } - .client-id-error { color: #cc1818; margin: -16px 0 24px; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js index 9b29815f7..6aabd15fd 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Components/AdvancedOptionsForm.js @@ -145,6 +145,7 @@ const AdvancedOptionsForm = () => { ) } showIcon={ false } variant="secondary" + className="small-button" isSandbox={ true /* This button always connects to sandbox */ } @@ -190,7 +191,11 @@ const AdvancedOptionsForm = () => { onChange={ setClientSecret } type="password" /> -
From ebfd4b18878d4fa9b27c3ab408c89cb38783e88d Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 11 Dec 2024 09:23:25 +0100 Subject: [PATCH 74/93] Remove "at no extra cost to you" text from Pay later. --- .../js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js | 4 ++-- .../js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js index 8d4964ce9..9779e789e 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js @@ -66,7 +66,7 @@ const AcdcFlow = ( { description={ sprintf( // translators: %s: Link to PayPal business fees guide __( - 'Offer installment payment options and get paid upfront - at no extra cost to you. Learn more', + 'Offer installment payment options and get paid upfront. Learn more', 'woocommerce-paypal-payments' ), 'https://www.paypal.com/us/business/paypal-business-fees' @@ -256,7 +256,7 @@ const AcdcFlow = ( { description={ sprintf( // translators: %s: Link to PayPal REST application guide __( - 'Offer installment payment options and get paid upfront - at no extra cost to you. Learn more', + 'Offer installment payment options and get paid upfront. Learn more', 'woocommerce-paypal-payments' ), 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js index 325e40a5e..99abfc4f2 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js @@ -60,7 +60,7 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { description={ sprintf( // translators: %s: Link to PayPal REST application guide __( - 'Offer installment payment options and get paid upfront - at no extra cost to you. Learn more', + 'Offer installment payment options and get paid upfront. Learn more', 'woocommerce-paypal-payments' ), 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' @@ -158,7 +158,7 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { description={ sprintf( // translators: %s: Link to PayPal REST application guide __( - 'Offer installment payment options and get paid upfront - at no extra cost to you. Learn more', + 'Offer installment payment options and get paid upfront. Learn more', 'woocommerce-paypal-payments' ), 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' From 1b1546768b1dbebc7b493859e103a638fe5a628e Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 11 Dec 2024 09:31:52 +0100 Subject: [PATCH 75/93] Make select box content relative, so it jumps above checkbox --- .../css/components/reusable-components/_select-box.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss index fbfb2c7e0..8dd9e355e 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss @@ -57,6 +57,7 @@ &__content { display: flex; + position: relative; } &__title { From 825e1d0f7353da6a26fa7c93938a8f684ed2a2fb Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 16:56:05 +0100 Subject: [PATCH 76/93] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Undo=20last=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Screens/Onboarding/Onboarding.js | 15 --------------- .../resources/js/Components/Screens/Settings.js | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js index 4d4a97fd3..e59bcdeeb 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -3,7 +3,6 @@ import { OnboardingHooks } from '../../../data'; import { getSteps, getCurrentStep } from './availableSteps'; import Navigation from './Components/Navigation'; -import { useEffect } from '@wordpress/element'; const Onboarding = () => { const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps(); @@ -11,20 +10,6 @@ const Onboarding = () => { const Steps = getSteps( flags ); const currentStep = getCurrentStep( step, Steps ); - // Disable the "Changes you made might not be saved" browser warning. - useEffect( () => { - const suppressBeforeUnload = ( event ) => { - event.stopImmediatePropagation(); - return undefined; - }; - - window.addEventListener( 'beforeunload', suppressBeforeUnload ); - - return () => { - window.removeEventListener( 'beforeunload', suppressBeforeUnload ); - }; - }, [] ); - const handleNext = () => setStep( currentStep.nextStep ); const handlePrev = () => setStep( currentStep.prevStep ); const handleExit = () => { diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js index e0634343c..faf65c047 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Settings.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Settings.js @@ -1,3 +1,5 @@ +import { useEffect } from '@wordpress/element'; + import { OnboardingHooks } from '../../data'; import Onboarding from './Onboarding/Onboarding'; import SettingsScreen from './SettingsScreen'; @@ -5,6 +7,20 @@ import SettingsScreen from './SettingsScreen'; const Settings = () => { const onboardingProgress = OnboardingHooks.useSteps(); + // Disable the "Changes you made might not be saved" browser warning. + useEffect( () => { + const suppressBeforeUnload = ( event ) => { + event.stopImmediatePropagation(); + return undefined; + }; + + window.addEventListener( 'beforeunload', suppressBeforeUnload ); + + return () => { + window.removeEventListener( 'beforeunload', suppressBeforeUnload ); + }; + }, [] ); + if ( ! onboardingProgress.isReady ) { // TODO: Use better loading state indicator. return
Loading...
; From ba0855301ee0201688b42f2c656d718eb8d16e94 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 19:02:29 +0100 Subject: [PATCH 77/93] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20code=20style=20cha?= =?UTF-8?q?nge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ppcp-wc-gateway/src/Settings/Settings.php | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php index d5465eb72..c33f47f42 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Settings.php +++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php @@ -5,7 +5,7 @@ * @package WooCommerce\PayPalCommerce\WcGateway\Settings */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Settings; @@ -18,44 +18,46 @@ */ class Settings implements ContainerInterface { - const KEY = 'woocommerce-ppcp-settings'; + const KEY = 'woocommerce-ppcp-settings'; + const CONNECTION_TAB_ID = 'ppcp-connection'; - const PAY_LATER_TAB_ID = 'ppcp-pay-later'; + + const PAY_LATER_TAB_ID = 'ppcp-pay-later'; /** * The settings. * * @var array */ - private $settings = array(); + private array $settings = array(); /** * The list of selected default button locations. * * @var string[] */ - protected $default_button_locations; + protected array $default_button_locations; /** * The list of selected default pay later button locations. * * @var string[] */ - protected $default_pay_later_button_locations; + protected array $default_pay_later_button_locations; /** * The list of selected default pay later messaging locations. * * @var string[] */ - protected $default_pay_later_messaging_locations; + protected array $default_pay_later_messaging_locations; /** * The default ACDC gateway title. * * @var string */ - protected $default_dcc_gateway_title; + protected string $default_dcc_gateway_title; /** * A helper for mapping the new/old settings. @@ -67,11 +69,17 @@ class Settings implements ContainerInterface { /** * Settings constructor. * - * @param string[] $default_button_locations The list of selected default button locations. - * @param string $default_dcc_gateway_title The default ACDC gateway title. - * @param string[] $default_pay_later_button_locations The list of selected default pay later button locations. - * @param string[] $default_pay_later_messaging_locations The list of selected default pay later messaging locations. - * @param SettingsMapHelper $settings_map_helper A helper for mapping the new/old settings. + * @param string[] $default_button_locations The list of selected default + * button locations. + * @param string $default_dcc_gateway_title The default ACDC gateway + * title. + * @param string[] $default_pay_later_button_locations The list of selected default + * pay later button locations. + * @param string[] $default_pay_later_messaging_locations The list of selected default + * pay later messaging + * locations. + * @param SettingsMapHelper $settings_map_helper A helper for mapping the + * new/old settings. */ public function __construct( array $default_button_locations, @@ -90,12 +98,13 @@ public function __construct( /** * Returns the value for an id. * - * @param string $id The value identificator. + * @throws NotFoundException When nothing was found. + * + * @param string $id The value identifier. * * @return mixed - * @throws NotFoundException When nothing was found. */ - public function get( $id ) { + public function get( string $id ) { if ( ! $this->has( $id ) ) { throw new NotFoundException(); } @@ -106,26 +115,27 @@ public function get( $id ) { /** * Whether a value exists. * - * @param string $id The value identificator. + * @param string $id The value identifier. * * @return bool */ - public function has( $id ) { + public function has( string $id ) : bool { if ( $this->settings_map_helper->has_mapped_key( $id ) ) { return true; } $this->load(); + return array_key_exists( $id, $this->settings ); } /** * Sets a value. * - * @param string $id The value identificator. + * @param string $id The value identifier. * @param mixed $value The value. */ - public function set( $id, $value ) { + public function set( string $id, $value ) : void { $this->load(); $this->settings[ $id ] = $value; } @@ -133,18 +143,18 @@ public function set( $id, $value ) { /** * Stores the settings to the database. */ - public function persist() { + public function persist() : bool { return update_option( self::KEY, $this->settings ); } /** * Loads the settings. * - * @return bool + * @return void */ - private function load(): bool { + private function load() : void { if ( $this->settings ) { - return false; + return; } $this->settings = get_option( self::KEY, array() ); @@ -175,6 +185,6 @@ private function load(): bool { } $this->settings[ $key ] = $value; } - return true; + } } From 236ce783cb12285957a313dfcb175e12855e9beb Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 19:06:13 +0100 Subject: [PATCH 78/93] =?UTF-8?q?=F0=9F=8E=A8=20Update=20variable=20name?= =?UTF-8?q?=20and=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index 474c6f533..0f171ee5d 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -5,7 +5,7 @@ * @package WooCommerce\PayPalCommerce\Compat */ -declare(strict_types=1); +declare( strict_types = 1 ); namespace WooCommerce\PayPalCommerce\Compat; @@ -33,17 +33,19 @@ public function __construct( array $settings_map ) { /** * Retrieves the mapped value from the new settings. * - * @param string $key The key. + * @param string $old_key The key from the legacy settings. + * * @return ?mixed the mapped value or Null if it doesn't exist. */ - public function mapped_value( string $key ) { - if ( ! $this->has_mapped_key( $key ) ) { + public function mapped_value( string $old_key ) { + if ( ! $this->has_mapped_key( $old_key ) ) { return null; } foreach ( $this->settings_map as $settings_map ) { - $mapped_key = array_search( $key, $settings_map->get_map(), true ); + $mapped_key = array_search( $old_key, $settings_map->get_map(), true ); $new_settings = $settings_map->get_model()->to_array(); + if ( ! empty( $new_settings[ $mapped_key ] ) ) { return $new_settings[ $mapped_key ]; } @@ -55,12 +57,13 @@ public function mapped_value( string $key ) { /** * Checks if the given key exists in the new settings. * - * @param string $key The key. + * @param string $old_key The key from the legacy settings. + * * @return bool true if the given key exists in the new settings, otherwise false. */ - public function has_mapped_key( string $key ) : bool { + public function has_mapped_key( string $old_key ) : bool { foreach ( $this->settings_map as $settings_map ) { - if ( in_array( $key, $settings_map->get_map(), true ) ) { + if ( in_array( $old_key, $settings_map->get_map(), true ) ) { return true; } } From 1c148471e7cf5c20b16a2d76fb86c1ae58d42567 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 20:56:55 +0100 Subject: [PATCH 79/93] =?UTF-8?q?=F0=9F=92=A1=20Update=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index 0f171ee5d..ed66d03cf 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -10,12 +10,16 @@ namespace WooCommerce\PayPalCommerce\Compat; /** - * A helper for mapping the new/old settings. + * A helper class to manage the transition between legacy and new settings. + * + * This utility provides mapping from old setting keys to new ones and retrieves + * their corresponding values from the appropriate models. The class uses lazy + * loading and caching to optimize performance during runtime. */ class SettingsMapHelper { /** - * A list of mapped settings. + * A list of settings maps containing mapping definitions. * * @var SettingsMap[] */ @@ -24,18 +28,18 @@ class SettingsMapHelper { /** * Constructor. * - * @param SettingsMap[] $settings_map A list of mapped settings. + * @param SettingsMap[] $settings_map A list of settings maps containing key definitions. */ public function __construct( array $settings_map ) { $this->settings_map = $settings_map; } /** - * Retrieves the mapped value from the new settings. + * Retrieves the value of a mapped key from the new settings. * * @param string $old_key The key from the legacy settings. * - * @return ?mixed the mapped value or Null if it doesn't exist. + * @return mixed|null The value of the mapped setting, or null if not found. */ public function mapped_value( string $old_key ) { if ( ! $this->has_mapped_key( $old_key ) ) { @@ -55,11 +59,11 @@ public function mapped_value( string $old_key ) { } /** - * Checks if the given key exists in the new settings. + * Determines if a given legacy key exists in the new settings. * * @param string $old_key The key from the legacy settings. * - * @return bool true if the given key exists in the new settings, otherwise false. + * @return bool True if the key exists in the new settings, false otherwise. */ public function has_mapped_key( string $old_key ) : bool { foreach ( $this->settings_map as $settings_map ) { From e1c775796a4e771f830858722190cd5c49916754 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 21:02:04 +0100 Subject: [PATCH 80/93] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Add=20mapping-cache?= =?UTF-8?q?=20for=20=E2=80=9Chas=5Fmapped=5Fkey=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index ed66d03cf..464ae9aba 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -25,6 +25,13 @@ class SettingsMapHelper { */ protected array $settings_map; + /** + * Indexed map for faster lookups, initialized lazily. + * + * @var array|null Associative array where old keys map to metadata. + */ + protected ?array $key_to_model = null; + /** * Constructor. * @@ -66,12 +73,42 @@ public function mapped_value( string $old_key ) { * @return bool True if the key exists in the new settings, false otherwise. */ public function has_mapped_key( string $old_key ) : bool { - foreach ( $this->settings_map as $settings_map ) { - if ( in_array( $old_key, $settings_map->get_map(), true ) ) { - return true; - } + $this->ensure_map_initialized(); + + return isset( $this->key_to_model[ $old_key ] ); + } + + /** + * Ensures the map of old-to-new settings is initialized. + * + * This method initializes the `key_to_model` array lazily to improve performance. + * + * @return void + */ + protected function ensure_map_initialized() : void { + if ( $this->key_to_model === null ) { + $this->initialize_key_map(); } + } + + /** + * Initializes the indexed map of old-to-new settings keys. + * + * This method processes the provided settings maps and indexes the legacy + * keys to their corresponding metadata for efficient lookup. + * + * @return void + */ + protected function initialize_key_map() : void { + $this->key_to_model = array(); - return false; + foreach ( $this->settings_map as $settings_map_instance ) { + foreach ( $settings_map_instance->get_map() as $old_key => $new_key ) { + $this->key_to_model[ $old_key ] = array( + 'new_key' => $new_key, + 'model' => $settings_map_instance->get_model(), + ); + } + } } } From dc7e318a1a761aaf0dfca76679958932a0e614c4 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 21:08:13 +0100 Subject: [PATCH 81/93] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Add=20cache=20for=20?= =?UTF-8?q?=E2=80=9Cmapped=5Fvalue=E2=80=9D=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index 464ae9aba..b79bcfe39 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -32,6 +32,13 @@ class SettingsMapHelper { */ protected ?array $key_to_model = null; + /** + * Cache for results of `to_array()` calls on models. + * + * @var array Associative array where keys are model IDs. + */ + protected array $model_cache = array(); + /** * Constructor. * @@ -49,20 +56,16 @@ public function __construct( array $settings_map ) { * @return mixed|null The value of the mapped setting, or null if not found. */ public function mapped_value( string $old_key ) { - if ( ! $this->has_mapped_key( $old_key ) ) { + $this->ensure_map_initialized(); + + if ( ! isset( $this->key_to_model[ $old_key ] ) ) { return null; } - foreach ( $this->settings_map as $settings_map ) { - $mapped_key = array_search( $old_key, $settings_map->get_map(), true ); - $new_settings = $settings_map->get_model()->to_array(); - - if ( ! empty( $new_settings[ $mapped_key ] ) ) { - return $new_settings[ $mapped_key ]; - } - } + $mapping = $this->key_to_model[ $old_key ]; + $model_id = spl_object_id( $mapping['model'] ); - return null; + return $this->get_cached_model_value( $model_id, $mapping['new_key'], $mapping['model'] ); } /** @@ -78,6 +81,23 @@ public function has_mapped_key( string $old_key ) : bool { return isset( $this->key_to_model[ $old_key ] ); } + /** + * Retrieves a cached model value or caches it if not already cached. + * + * @param int $model_id The unique identifier for the model object. + * @param string $new_key The key in the new settings structure. + * @param object $model The model object. + * + * @return mixed|null The value of the key in the model, or null if not found. + */ + protected function get_cached_model_value( int $model_id, string $new_key, object $model ) { + if ( ! isset( $this->model_cache[ $model_id ] ) ) { + $this->model_cache[ $model_id ] = $model->to_array(); + } + + return $this->model_cache[ $model_id ][ $new_key ] ?? null; + } + /** * Ensures the map of old-to-new settings is initialized. * From cbf1c0656adc8f5bb7e1edbdac5a73456a2fba76 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 21:15:01 +0100 Subject: [PATCH 82/93] =?UTF-8?q?=F0=9F=A5=85=20Prevent=20invalid=20settin?= =?UTF-8?q?gs-mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index b79bcfe39..7db666ac4 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -9,6 +9,8 @@ namespace WooCommerce\PayPalCommerce\Compat; +use RuntimeException; + /** * A helper class to manage the transition between legacy and new settings. * @@ -42,12 +44,35 @@ class SettingsMapHelper { /** * Constructor. * + * @throws RuntimeException When an old key has multiple mappings. + * * @param SettingsMap[] $settings_map A list of settings maps containing key definitions. */ public function __construct( array $settings_map ) { + $this->validate_settings_map( $settings_map ); $this->settings_map = $settings_map; } + /** + * Validates the settings map for duplicate keys. + * + * @throws RuntimeException When an old key has multiple mappings. + * + * @param SettingsMap[] $settings_map The settings map to validate. + */ + protected function validate_settings_map( array $settings_map ) : void { + $seen_keys = array(); + + foreach ( $settings_map as $settings_map_instance ) { + foreach ( $settings_map_instance->get_map() as $old_key => $new_key ) { + if ( isset( $seen_keys[ $old_key ] ) ) { + throw new RuntimeException( "Duplicate mapping for legacy key '$old_key'." ); + } + $seen_keys[ $old_key ] = true; + } + } + } + /** * Retrieves the value of a mapped key from the new settings. * From 3b2e0687b84166581d97525a221757574d5601d1 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 21:26:10 +0100 Subject: [PATCH 83/93] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Revert=20type=20anno?= =?UTF-8?q?tations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-wc-gateway/src/Settings/Settings.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php index c33f47f42..182fba281 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Settings.php +++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php @@ -104,7 +104,7 @@ public function __construct( * * @return mixed */ - public function get( string $id ) { + public function get( $id ) { if ( ! $this->has( $id ) ) { throw new NotFoundException(); } @@ -119,7 +119,7 @@ public function get( string $id ) { * * @return bool */ - public function has( string $id ) : bool { + public function has( string $id ) { if ( $this->settings_map_helper->has_mapped_key( $id ) ) { return true; } @@ -135,7 +135,7 @@ public function has( string $id ) : bool { * @param string $id The value identifier. * @param mixed $value The value. */ - public function set( string $id, $value ) : void { + public function set( $id, $value ) { $this->load(); $this->settings[ $id ] = $value; } @@ -143,18 +143,18 @@ public function set( string $id, $value ) : void { /** * Stores the settings to the database. */ - public function persist() : bool { + public function persist() { return update_option( self::KEY, $this->settings ); } /** * Loads the settings. * - * @return void + * @return bool */ - private function load() : void { + private function load() : bool { if ( $this->settings ) { - return; + return false; } $this->settings = get_option( self::KEY, array() ); @@ -186,5 +186,6 @@ private function load() : void { $this->settings[ $key ] = $value; } + return true; } } From 169c184e57e4bbb105bb810678f14792046470f0 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Thu, 12 Dec 2024 06:26:07 +0100 Subject: [PATCH 84/93] Make the whole rdb box selectable and link clickable --- .../css/components/reusable-components/_select-box.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss index 8dd9e355e..410d9e9d0 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_select-box.scss @@ -58,6 +58,13 @@ &__content { display: flex; position: relative; + pointer-events: none; + *:not(a){ + pointer-events: none; + } + a { + pointer-events: all; + } } &__title { From e8ad1a5e318997dd8948253be7f53bb0233c32c3 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 13:59:38 +0100 Subject: [PATCH 85/93] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20default=20order=20?= =?UTF-8?q?in=20doc-blocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-compat/src/SettingsMapHelper.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-compat/src/SettingsMapHelper.php b/modules/ppcp-compat/src/SettingsMapHelper.php index 7db666ac4..8cedc104c 100644 --- a/modules/ppcp-compat/src/SettingsMapHelper.php +++ b/modules/ppcp-compat/src/SettingsMapHelper.php @@ -44,9 +44,8 @@ class SettingsMapHelper { /** * Constructor. * - * @throws RuntimeException When an old key has multiple mappings. - * * @param SettingsMap[] $settings_map A list of settings maps containing key definitions. + * @throws RuntimeException When an old key has multiple mappings. */ public function __construct( array $settings_map ) { $this->validate_settings_map( $settings_map ); @@ -56,9 +55,8 @@ public function __construct( array $settings_map ) { /** * Validates the settings map for duplicate keys. * - * @throws RuntimeException When an old key has multiple mappings. - * * @param SettingsMap[] $settings_map The settings map to validate. + * @throws RuntimeException When an old key has multiple mappings. */ protected function validate_settings_map( array $settings_map ) : void { $seen_keys = array(); From e59ae23c43c67ec64aae93519e8b9962bcbb4d2c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 16:55:19 +0100 Subject: [PATCH 86/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Replace=20generatePr?= =?UTF-8?q?iceText=20with=20custom=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/ReusableComponents/BadgeBox.js | 9 +- .../AcdcOptionalPaymentMethods.js | 122 ++++++++++-------- .../BcdcOptionalPaymentMethods.js | 30 +++-- .../ReusableComponents/PricingTitleBadge.js | 29 +++++ .../WelcomeDocs/AcdcFlow.js | 43 +++--- .../WelcomeDocs/BcdcFlow.js | 30 +++-- .../WelcomeDocs/WelcomeDocs.js | 3 +- .../resources/js/utils/badgeBoxUtils.js | 18 --- 8 files changed, 159 insertions(+), 125 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js delete mode 100644 modules/ppcp-settings/resources/js/utils/badgeBoxUtils.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js index 5a257b22e..24dc36134 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/BadgeBox.js @@ -1,6 +1,4 @@ import data from '../../utils/data'; -import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; -import { __ } from '@wordpress/i18n'; const BadgeBox = ( props ) => { const titleSize = @@ -29,12 +27,7 @@ const BadgeBox = ( props ) => { ) } - { props.textBadge && ( - - ) } + { props.textBadge }
{ props?.description && ( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js index 2abcc37a9..48562d372 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js @@ -1,8 +1,8 @@ -import BadgeBox from '../BadgeBox'; import { __, sprintf } from '@wordpress/i18n'; + +import BadgeBox from '../BadgeBox'; import Separator from '../Separator'; -import generatePriceText from '../../../utils/badgeBoxUtils'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; +import PricingTitleBadge from '../PricingTitleBadge'; const AcdcOptionalPaymentMethods = ( { isFastlane, @@ -24,11 +24,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ generatePriceText( - 'ccf', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -48,11 +50,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ generatePriceText( - 'dw', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -73,11 +77,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ generatePriceText( - 'apm', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -91,11 +97,9 @@ const AcdcOptionalPaymentMethods = ( { + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -123,11 +127,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ generatePriceText( - 'ccf', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -147,11 +153,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ generatePriceText( - 'dw', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -173,11 +181,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ generatePriceText( - 'apm', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -204,11 +214,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ generatePriceText( - 'ccf', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -225,11 +237,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ generatePriceText( - 'dw', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -251,11 +265,13 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ generatePriceText( - 'apm', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js index b5e4b7d2e..3fb321387 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js @@ -1,7 +1,7 @@ -import BadgeBox from '../BadgeBox'; import { __, sprintf } from '@wordpress/i18n'; -import generatePriceText from '../../../utils/badgeBoxUtils'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; + +import BadgeBox from '../BadgeBox'; +import PricingTitleBadge from '../PricingTitleBadge'; const BcdcOptionalPaymentMethods = ( { isPayLater, @@ -22,11 +22,13 @@ const BcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ generatePriceText( - 'standardCardFields', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal REST application guide __( @@ -53,11 +55,13 @@ const BcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ generatePriceText( - 'standardCardFields', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ sprintf( // translators: %s: Link to PayPal REST application guide __( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js new file mode 100644 index 000000000..293ec361e --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js @@ -0,0 +1,29 @@ +import { __, sprintf } from '@wordpress/i18n'; +import { countryPriceInfo } from '../../utils/countryPriceInfo'; +import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; +import { CommonHooks } from '../../data'; + +const PricingTitleBadge = ( { item, country, currency } ) => { + const infos = countryPriceInfo[ country ]; + + if ( ! infos || ! infos[ item ] ) { + return null; + } + + const percentage = infos[ item ].toFixed( 2 ); + const fixedFee = `${ infos.currencySymbol }${ infos.fixedFee }`; + + const label = sprintf( + __( + 'from %1$s%% + %2$s %2$s1', + 'woocommerce-paypal-payments' + ), + percentage, + fixedFee, + currency + ); + + return ; +}; + +export default PricingTitleBadge; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js index 9779e789e..0cb62de5e 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js @@ -1,10 +1,9 @@ -import BadgeBox, { BADGE_BOX_TITLE_BIG } from '../BadgeBox'; import { __, sprintf } from '@wordpress/i18n'; -import Separator from '../Separator'; -import generatePriceText from '../../../utils/badgeBoxUtils'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; +import BadgeBox, { BADGE_BOX_TITLE_BIG } from '../BadgeBox'; +import Separator from '../Separator'; import OptionalPaymentMethods from '../OptionalPaymentMethods/OptionalPaymentMethods'; +import PricingTitleBadge from '../PricingTitleBadge'; const AcdcFlow = ( { isFastlane, @@ -22,11 +21,13 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ generatePriceText( - 'checkout', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -133,11 +134,13 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ generatePriceText( - 'checkout', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -217,11 +220,13 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ generatePriceText( - 'checkout', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js index 99abfc4f2..8feda46fa 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js @@ -1,9 +1,9 @@ -import BadgeBox, { BADGE_BOX_TITLE_BIG } from '../BadgeBox'; import { __, sprintf } from '@wordpress/i18n'; + +import BadgeBox, { BADGE_BOX_TITLE_BIG } from '../BadgeBox'; import Separator from '../Separator'; -import generatePriceText from '../../../utils/badgeBoxUtils'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; import OptionalPaymentMethods from '../OptionalPaymentMethods/OptionalPaymentMethods'; +import PricingTitleBadge from '../PricingTitleBadge'; const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { if ( isPayLater && storeCountry === 'US' ) { @@ -16,11 +16,13 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ generatePriceText( - 'checkout', - countryPriceInfo[ storeCountry ], - storeCurrency - ) } + textBadge={ + + } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -122,11 +124,13 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { + } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js index eabb5b3db..0e89b72be 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js @@ -1,7 +1,8 @@ import { __ } from '@wordpress/i18n'; + +import { countryPriceInfo } from '../../../utils/countryPriceInfo'; import AcdcFlow from './AcdcFlow'; import BcdcFlow from './BcdcFlow'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; import { pricesBasedDescription } from './pricesBasedDescription'; const WelcomeDocs = ( { diff --git a/modules/ppcp-settings/resources/js/utils/badgeBoxUtils.js b/modules/ppcp-settings/resources/js/utils/badgeBoxUtils.js deleted file mode 100644 index 60c4da274..000000000 --- a/modules/ppcp-settings/resources/js/utils/badgeBoxUtils.js +++ /dev/null @@ -1,18 +0,0 @@ -import { __ } from '@wordpress/i18n'; - -const generatePriceText = ( type, selectedCountryPrice, storeCurrency ) => { - if ( ! selectedCountryPrice || ! selectedCountryPrice[ type ] ) { - console.warn( `Invalid type or price data for: ${ type }` ); - return ''; - } - - const percentage = selectedCountryPrice[ type ].toFixed( 2 ); - const fixedFee = `${ selectedCountryPrice.currencySymbol }${ selectedCountryPrice.fixedFee }`; - - return __( - `from ${ percentage }% + ${ fixedFee } ${ storeCurrency }1`, - 'woocommerce-paypal-payments' - ); -}; - -export default generatePriceText; From 561f71f0b5ce539bebf6126e9ae0d8c15ab91829 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 17:02:48 +0100 Subject: [PATCH 87/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20price=20de?= =?UTF-8?q?scription=20to=20custom=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/PricingDescription.js | 27 +++++++++++++++++++ .../WelcomeDocs/WelcomeDocs.js | 12 ++------- .../Screens/Onboarding/StepPaymentMethods.js | 12 ++------- 3 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js new file mode 100644 index 000000000..f9ef5f2e7 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js @@ -0,0 +1,27 @@ +import { __, sprintf } from '@wordpress/i18n'; + +import { countryPriceInfo } from '../../utils/countryPriceInfo'; + +const PricingDescription = ( { country } ) => { + if ( ! countryPriceInfo[ country ] ) { + return null; + } + + const label = sprintf( + // translators: %s: Link to PayPal REST application guide + __( + '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', + 'woocommerce-paypal-payments' + ), + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input' + ); + + return ( +

+ ); +}; + +export default PricingDescription; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js index 0e89b72be..43ce3360e 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js @@ -1,9 +1,8 @@ import { __ } from '@wordpress/i18n'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; +import PricingDescription from '../PricingDescription'; import AcdcFlow from './AcdcFlow'; import BcdcFlow from './BcdcFlow'; -import { pricesBasedDescription } from './pricesBasedDescription'; const WelcomeDocs = ( { useAcdc, @@ -34,14 +33,7 @@ const WelcomeDocs = ( { storeCurrency={ storeCurrency } /> ) } - { storeCountry in countryPriceInfo && ( -

- ) } +
); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js index e94f176f7..8e2455961 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js @@ -5,8 +5,7 @@ import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; import SelectBox from '../../ReusableComponents/SelectBox'; import { CommonHooks, OnboardingHooks } from '../../../data'; import OptionalPaymentMethods from '../../ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods'; -import { pricesBasedDescription } from '../../ReusableComponents/WelcomeDocs/pricesBasedDescription'; -import { countryPriceInfo } from '../../../utils/countryPriceInfo'; +import PricingDescription from '../../ReusableComponents/PricingDescription'; const OPM_RADIO_GROUP_NAME = 'optional-payment-methods'; @@ -60,14 +59,7 @@ const StepPaymentMethods = ( {} ) => { type="radio" > - { storeCountry in countryPriceInfo && ( -

- ) } +
); From fe978a5ea01b5544ad9fdd55bc6ebe25bfc717db Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 17:35:18 +0100 Subject: [PATCH 88/93] =?UTF-8?q?=E2=9C=A8=20Introduce=20date-variable=20i?= =?UTF-8?q?n=20pricing=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/PricingDescription.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js index f9ef5f2e7..e61605014 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js @@ -7,20 +7,28 @@ const PricingDescription = ( { country } ) => { return null; } + const lastDate = 'October 25th, 2024'; // TODO -- needs to be the last plugin update date. + const detailsUrl = + 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input'; + const label = sprintf( - // translators: %s: Link to PayPal REST application guide + // translators: %1$s: Pricing date, %2$s Link to PayPal price-details page. __( - '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', + 'Prices based on domestic transactions as of %1$s. Click here for full pricing details.', 'woocommerce-paypal-payments' ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input' + lastDate, + detailsUrl ); return (

+ data-country={ storeCountry } + > + 1 + +

); }; From b89164e1ead4022b41a2c97f74dece5cc7bb5c33 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 17:48:09 +0100 Subject: [PATCH 89/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20country=20che?= =?UTF-8?q?ck=20into=20PricingDescription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/ReusableComponents/PricingDescription.js | 7 +++++-- .../ReusableComponents/WelcomeDocs/WelcomeDocs.js | 2 +- .../js/Components/Screens/Onboarding/StepPaymentMethods.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js index e61605014..a41ad8f3a 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingDescription.js @@ -1,9 +1,12 @@ import { __, sprintf } from '@wordpress/i18n'; import { countryPriceInfo } from '../../utils/countryPriceInfo'; +import { CommonHooks } from '../../data'; -const PricingDescription = ( { country } ) => { - if ( ! countryPriceInfo[ country ] ) { +const PricingDescription = () => { + const { storeCountry } = CommonHooks.useWooSettings(); + + if ( ! countryPriceInfo[ storeCountry ] ) { return null; } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js index 43ce3360e..fc3f284b5 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js @@ -33,7 +33,7 @@ const WelcomeDocs = ( { storeCurrency={ storeCurrency } /> ) } - +
); }; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js index 8e2455961..42050842b 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js @@ -59,7 +59,7 @@ const StepPaymentMethods = ( {} ) => { type="radio" > - +
); From 5bd3e5f976ce2595e71e5d94475a0e3cbf709756 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 17:54:38 +0100 Subject: [PATCH 90/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20country=20log?= =?UTF-8?q?ic=20into=20PricingTitleBadge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AcdcOptionalPaymentMethods.js | 73 +++---------------- .../BcdcOptionalPaymentMethods.js | 20 +---- .../OptionalPaymentMethods.js | 3 - .../ReusableComponents/PricingTitleBadge.js | 8 +- .../WelcomeDocs/AcdcFlow.js | 34 +-------- .../WelcomeDocs/BcdcFlow.js | 20 +---- .../WelcomeDocs/WelcomeDocs.js | 10 +-- .../Screens/Onboarding/StepPaymentMethods.js | 2 +- .../Screens/Onboarding/StepWelcome.js | 3 +- 9 files changed, 27 insertions(+), 146 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js index 48562d372..6066ac470 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/AcdcOptionalPaymentMethods.js @@ -8,7 +8,6 @@ const AcdcOptionalPaymentMethods = ( { isFastlane, isPayLater, storeCountry, - storeCurrency, } ) => { if ( isFastlane && isPayLater && storeCountry === 'US' ) { return ( @@ -24,13 +23,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -50,13 +43,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -77,13 +64,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -127,13 +108,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -153,13 +128,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -181,13 +150,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -214,13 +177,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -237,13 +194,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-apple-pay.svg', 'icon-button-google-pay.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( @@ -265,13 +216,7 @@ const AcdcOptionalPaymentMethods = ( { 'icon-button-blik.svg', 'icon-button-bancontact.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal business fees guide __( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js index 3fb321387..4307636e2 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/BcdcOptionalPaymentMethods.js @@ -3,11 +3,7 @@ import { __, sprintf } from '@wordpress/i18n'; import BadgeBox from '../BadgeBox'; import PricingTitleBadge from '../PricingTitleBadge'; -const BcdcOptionalPaymentMethods = ( { - isPayLater, - storeCountry, - storeCurrency, -} ) => { +const BcdcOptionalPaymentMethods = ( { isPayLater, storeCountry } ) => { if ( isPayLater && storeCountry === 'us' ) { return (
@@ -23,11 +19,7 @@ const BcdcOptionalPaymentMethods = ( { 'icon-button-discover.svg', ] } textBadge={ - + } description={ sprintf( // translators: %s: Link to PayPal REST application guide @@ -55,13 +47,7 @@ const BcdcOptionalPaymentMethods = ( { 'icon-button-amex.svg', 'icon-button-discover.svg', ] } - textBadge={ - - } + textBadge={ } description={ sprintf( // translators: %s: Link to PayPal REST application guide __( diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods.js index 129088f59..b83fad366 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods.js @@ -6,7 +6,6 @@ const OptionalPaymentMethods = ( { isFastlane, isPayLater, storeCountry, - storeCurrency, } ) => { return (
@@ -15,13 +14,11 @@ const OptionalPaymentMethods = ( { isFastlane={ isFastlane } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } /> ) : ( ) }
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js index 293ec361e..37a2b57d7 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js @@ -1,10 +1,12 @@ import { __, sprintf } from '@wordpress/i18n'; + import { countryPriceInfo } from '../../utils/countryPriceInfo'; import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; import { CommonHooks } from '../../data'; -const PricingTitleBadge = ( { item, country, currency } ) => { - const infos = countryPriceInfo[ country ]; +const PricingTitleBadge = ( { item } ) => { + const { storeCountry } = CommonHooks.useWooSettings(); + const infos = countryPriceInfo[ storeCountry ]; if ( ! infos || ! infos[ item ] ) { return null; @@ -20,7 +22,7 @@ const PricingTitleBadge = ( { item, country, currency } ) => { ), percentage, fixedFee, - currency + infos.currencySymbol ); return ; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js index 0cb62de5e..89d4455e1 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/AcdcFlow.js @@ -5,12 +5,7 @@ import Separator from '../Separator'; import OptionalPaymentMethods from '../OptionalPaymentMethods/OptionalPaymentMethods'; import PricingTitleBadge from '../PricingTitleBadge'; -const AcdcFlow = ( { - isFastlane, - isPayLater, - storeCountry, - storeCurrency, -} ) => { +const AcdcFlow = ( { isFastlane, isPayLater, storeCountry } ) => { if ( isFastlane && isPayLater && storeCountry === 'US' ) { return (
@@ -21,13 +16,7 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ - - } + textBadge={ } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -117,7 +106,6 @@ const AcdcFlow = ( { isFastlane={ isFastlane } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } />
@@ -134,13 +122,7 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ - - } + textBadge={ } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -204,7 +186,6 @@ const AcdcFlow = ( { isFastlane={ isFastlane } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } />
@@ -220,13 +201,7 @@ const AcdcFlow = ( { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ - - } + textBadge={ } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -285,7 +260,6 @@ const AcdcFlow = ( { isFastlane={ isFastlane } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } />
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js index 8feda46fa..591412be3 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/BcdcFlow.js @@ -5,7 +5,7 @@ import Separator from '../Separator'; import OptionalPaymentMethods from '../OptionalPaymentMethods/OptionalPaymentMethods'; import PricingTitleBadge from '../PricingTitleBadge'; -const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { +const BcdcFlow = ( { isPayLater, storeCountry } ) => { if ( isPayLater && storeCountry === 'US' ) { return (
@@ -16,13 +16,7 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { 'woocommerce-paypal-payments' ) } titleType={ BADGE_BOX_TITLE_BIG } - textBadge={ - - } + textBadge={ } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -112,7 +106,6 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { isFastlane={ false } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } />
@@ -124,13 +117,7 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { - } + textBadge={ } description={ __( 'Our all-in-one checkout solution lets you offer PayPal, Venmo, Pay Later options, and more to help maximise conversion', 'woocommerce-paypal-payments' @@ -185,7 +172,6 @@ const BcdcFlow = ( { isPayLater, storeCountry, storeCurrency } ) => { isFastlane={ false } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } />
); diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js index fc3f284b5..cb8d2fe7b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/WelcomeDocs.js @@ -4,13 +4,7 @@ import PricingDescription from '../PricingDescription'; import AcdcFlow from './AcdcFlow'; import BcdcFlow from './BcdcFlow'; -const WelcomeDocs = ( { - useAcdc, - isFastlane, - isPayLater, - storeCountry, - storeCurrency, -} ) => { +const WelcomeDocs = ( { useAcdc, isFastlane, isPayLater, storeCountry } ) => { return (

@@ -24,13 +18,11 @@ const WelcomeDocs = ( { isFastlane={ isFastlane } isPayLater={ isPayLater } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } /> ) : ( ) } diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js index 42050842b..ac56180a3 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepPaymentMethods.js @@ -1,9 +1,9 @@ import { __ } from '@wordpress/i18n'; +import { CommonHooks, OnboardingHooks } from '../../../data'; import OnboardingHeader from '../../ReusableComponents/OnboardingHeader'; import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; import SelectBox from '../../ReusableComponents/SelectBox'; -import { CommonHooks, OnboardingHooks } from '../../../data'; import OptionalPaymentMethods from '../../ReusableComponents/OptionalPaymentMethods/OptionalPaymentMethods'; import PricingDescription from '../../ReusableComponents/PricingDescription'; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js index f8abf9ea5..f9b7ddea4 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -12,7 +12,7 @@ import { CommonHooks } from '../../../data'; import BusyStateWrapper from '../../ReusableComponents/BusyStateWrapper'; const StepWelcome = ( { setStep, currentStep } ) => { - const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + const { storeCountry } = CommonHooks.useWooSettings(); return (
@@ -54,7 +54,6 @@ const StepWelcome = ( { setStep, currentStep } ) => { isFastlane={ true } isPayLater={ true } storeCountry={ storeCountry } - storeCurrency={ storeCurrency } /> Date: Thu, 12 Dec 2024 17:54:49 +0100 Subject: [PATCH 91/93] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WelcomeDocs/pricesBasedDescription.js | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js deleted file mode 100644 index c4d3eb983..000000000 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/WelcomeDocs/pricesBasedDescription.js +++ /dev/null @@ -1,10 +0,0 @@ -import { __, sprintf } from '@wordpress/i18n'; - -export const pricesBasedDescription = sprintf( - // translators: %s: Link to PayPal REST application guide - __( - '1Prices based on domestic transactions as of October 25th, 2024. Click here for full pricing details.', - 'woocommerce-paypal-payments' - ), - 'https://woocommerce.com/document/woocommerce-paypal-payments/#manual-credential-input ' -); From bdb53dfced2462e0fce0565d046937730cd89aea Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 18:28:23 +0100 Subject: [PATCH 92/93] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Remove=20footnote-nu?= =?UTF-8?q?mber=20from=20translatable=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/PricingTitleBadge.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js index 37a2b57d7..3e89d40d6 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js @@ -16,16 +16,18 @@ const PricingTitleBadge = ( { item } ) => { const fixedFee = `${ infos.currencySymbol }${ infos.fixedFee }`; const label = sprintf( - __( - 'from %1$s%% + %2$s %2$s1', - 'woocommerce-paypal-payments' - ), + __( 'from %1$s%% + %2$s %3$s', 'woocommerce-paypal-payments' ), percentage, fixedFee, infos.currencySymbol ); - return ; + return ( + 1` } + /> + ); }; export default PricingTitleBadge; From 478002dc32366622953edaa213a4cc393573eb51 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 18:29:36 +0100 Subject: [PATCH 93/93] =?UTF-8?q?=E2=9C=A8=20Introduce=20price-matrix=20an?= =?UTF-8?q?d=20currency-formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/PricingTitleBadge.js | 20 +++++++--- .../resources/js/utils/countryPriceInfo.js | 40 +++++++++++-------- .../resources/js/utils/formatPrice.js | 34 ++++++++++++++++ 3 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/utils/formatPrice.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js index 3e89d40d6..33825d96b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/PricingTitleBadge.js @@ -1,8 +1,19 @@ import { __, sprintf } from '@wordpress/i18n'; +import { CommonHooks } from '../../data'; import { countryPriceInfo } from '../../utils/countryPriceInfo'; +import { formatPrice } from '../../utils/formatPrice'; import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; -import { CommonHooks } from '../../data'; + +const getFixedAmount = ( currency, priceList ) => { + if ( priceList[ currency ] ) { + return formatPrice( priceList[ currency ], currency ); + } + + const [ defaultCurrency, defaultPrice ] = Object.entries( priceList )[ 0 ]; + + return formatPrice( defaultPrice, defaultCurrency ); +}; const PricingTitleBadge = ( { item } ) => { const { storeCountry } = CommonHooks.useWooSettings(); @@ -13,13 +24,12 @@ const PricingTitleBadge = ( { item } ) => { } const percentage = infos[ item ].toFixed( 2 ); - const fixedFee = `${ infos.currencySymbol }${ infos.fixedFee }`; + const fixedAmount = getFixedAmount( storeCountry, infos.fixedFee ); const label = sprintf( - __( 'from %1$s%% + %2$s %3$s', 'woocommerce-paypal-payments' ), + __( 'from %1$s%% + %2$s', 'woocommerce-paypal-payments' ), percentage, - fixedFee, - infos.currencySymbol + fixedAmount ); return ( diff --git a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js index 34bfc8e7f..17504453b 100644 --- a/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js +++ b/modules/ppcp-settings/resources/js/utils/countryPriceInfo.js @@ -1,7 +1,8 @@ export const countryPriceInfo = { US: { - currencySymbol: '$', - fixedFee: 0.49, + fixedFee: { + USD: 0.49, + }, checkout: 3.49, ccf: 2.59, dw: 2.59, @@ -10,8 +11,9 @@ export const countryPriceInfo = { standardCardFields: 2.99, }, UK: { - currencySymbol: '£', - fixedFee: 0.3, + fixedFee: { + GPB: 0.3, + }, checkout: 2.9, ccf: 1.2, dw: 1.2, @@ -19,8 +21,9 @@ export const countryPriceInfo = { standardCardFields: 1.2, }, CA: { - currencySymbol: '$', - fixedFee: 0.3, + fixedFee: { + CAD: 0.3, + }, checkout: 2.9, ccf: 2.7, dw: 2.7, @@ -28,8 +31,9 @@ export const countryPriceInfo = { standardCardFields: 2.9, }, AU: { - currencySymbol: '$', - fixedFee: 0.3, + fixedFee: { + AUD: 0.3, + }, checkout: 2.6, ccf: 1.75, dw: 1.75, @@ -37,8 +41,9 @@ export const countryPriceInfo = { standardCardFields: 2.6, }, FR: { - currencySymbol: '€', - fixedFee: 0.35, + fixedFee: { + EUR: 0.35, + }, checkout: 2.9, ccf: 1.2, dw: 1.2, @@ -46,8 +51,9 @@ export const countryPriceInfo = { standardCardFields: 1.2, }, IT: { - currencySymbol: '€', - fixedFee: 0.35, + fixedFee: { + EUR: 0.35, + }, checkout: 3.4, ccf: 1.2, dw: 1.2, @@ -55,8 +61,9 @@ export const countryPriceInfo = { standardCardFields: 1.2, }, DE: { - currencySymbol: '€', - fixedFee: 0.39, + fixedFee: { + EUR: 0.39, + }, checkout: 2.99, ccf: 2.99, dw: 2.99, @@ -64,8 +71,9 @@ export const countryPriceInfo = { standardCardFields: 2.99, }, ES: { - currencySymbol: '€', - fixedFee: 0.35, + fixedFee: { + EUR: 0.35, + }, checkout: 2.9, ccf: 1.2, dw: 1.2, diff --git a/modules/ppcp-settings/resources/js/utils/formatPrice.js b/modules/ppcp-settings/resources/js/utils/formatPrice.js new file mode 100644 index 000000000..56e6b85d9 --- /dev/null +++ b/modules/ppcp-settings/resources/js/utils/formatPrice.js @@ -0,0 +1,34 @@ +const priceFormatInfo = { + USD: { + prefix: '$', + suffix: 'USD', + }, + CAD: { + prefix: '$', + suffix: 'CAD', + }, + AUD: { + prefix: '$', + suffix: 'AUD', + }, + EUR: { + prefix: '€', + suffix: '', + }, + GPB: { + prefix: '£', + suffix: '', + }, +}; + +export const formatPrice = ( value, currency ) => { + const currencyInfo = priceFormatInfo[ currency ]; + const amount = value.toFixed( 2 ); + + if ( ! currencyInfo ) { + console.error( `Unsupported currency: ${ currency }` ); + return amount; + } + + return `${ currencyInfo.prefix }${ amount } ${ currencyInfo.suffix }`; +};