From 94525193f6abfcbd7643162e5a1d32dc81915008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Fri, 29 Nov 2024 15:02:41 +0100 Subject: [PATCH 001/169] Add PHP 8.4 support to CI --- .github/workflows/php.yml | 2 +- composer.json | 6 +++--- composer.lock | 44 +++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 9bf4f02b0..5dfe9be79 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3'] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] name: PHP ${{ matrix.php-versions }} steps: diff --git a/composer.json b/composer.json index 4d4b9f2f2..af87af977 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "woocommerce/woocommerce-paypal-payments", "type": "wordpress-plugin", "description": "PayPal Commerce Platform for WooCommerce", - "license": "GPL-2.0", + "license": "GPL-2.0-or-later", "require": { "php": "^7.4 | ^8.0", "ext-json": "*", @@ -23,8 +23,8 @@ "woocommerce/woocommerce-sniffs": "^0.1.0", "phpunit/phpunit": "^7.0 | ^8.0 | ^9.0", "brain/monkey": "^2.4", - "php-stubs/wordpress-stubs": "^5.0@stable", - "php-stubs/woocommerce-stubs": "^8.0@stable", + "php-stubs/wordpress-stubs": "^v6.7.1", + "php-stubs/woocommerce-stubs": "^v8.9.1", "vimeo/psalm": "^4.0", "vlucas/phpdotenv": "^5" }, diff --git a/composer.lock b/composer.lock index a18b56927..91471c2bc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2fa610ed883c0868838d3008b7127cbf", + "content-hash": "89ec4e31d0bd8fc8770ffd065dff81e0", "packages": [ { "name": "container-interop/service-provider", @@ -2351,27 +2351,28 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v5.9.9", + "version": "v6.7.1", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059" + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/06c51c4863659ea9e9f4c2a23293728a677cb059", - "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/83448e918bf06d1ed3d67ceb6a985fc266a02fd1", + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1", "shasum": "" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0", + "php": "^7.4 || ^8.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "5.3", - "phpstan/phpstan": "^1.10.49", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -2392,9 +2393,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.9" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.7.1" }, - "time": "2024-04-14T17:16:00+00:00" + "time": "2024-11-24T03:57:09+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -3218,16 +3219,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.20", + "version": "9.6.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "49d7820565836236411f5dc002d16dd689cde42f" + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", - "reference": "49d7820565836236411f5dc002d16dd689cde42f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", "shasum": "" }, "require": { @@ -3242,7 +3243,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-code-coverage": "^9.2.32", "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.4", @@ -3301,7 +3302,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" }, "funding": [ { @@ -3317,7 +3318,7 @@ "type": "tidelift" } ], - "time": "2024-07-10T11:45:39+00:00" + "time": "2024-09-19T10:50:18+00:00" }, { "name": "sebastian/cli-parser", @@ -5540,17 +5541,14 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "php-stubs/wordpress-stubs": 0, - "php-stubs/woocommerce-stubs": 0 - }, + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^7.4 | ^8.0", "ext-json": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "7.4" }, From 448d0f6dcdf26544eaaade45ea14a56d427049dc Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 2 Dec 2024 18:36:47 +0400 Subject: [PATCH 002/169] Check if the "extended" section exists before adding items --- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index e8c35a5f6..4ca10c92f 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway; +use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; use Exception; use Psr\Log\LoggerInterface; use Throwable; @@ -867,10 +868,16 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $logger = $container->get( 'woocommerce.logger.woocommerce' ); assert( $logger instanceof LoggerInterface ); try { + $task_lists = TaskLists::get_lists(); + if ( ! isset( $task_lists['extended'] ) ) { + return; + } + $simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' ); if ( empty( $simple_redirect_tasks ) ) { return; } + $task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' ); assert( $task_registrar instanceof TaskRegistrarInterface ); From a06a74004c7e065e1c53d8174457269acf1fc0c0 Mon Sep 17 00:00:00 2001 From: Himad M Date: Mon, 2 Dec 2024 18:15:07 -0400 Subject: [PATCH 003/169] 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 array( 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 @@ class CommonSettings extends AbstractDataModel { 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 @@ class CommonRestEndpoint extends RestEndpoint { * * @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 004/169] 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 add776c976fd6300e88df7ab0559f0114f586b7e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 3 Dec 2024 12:27:34 +0100 Subject: [PATCH 005/169] Extract components --- .../js/Components/block-editor-paypal.js | 52 ++ .../resources/js/Components/paypal-label.js | 14 + .../resources/js/Components/paypal.js | 687 ++++++++++++++++ .../resources/js/checkout-block.js | 766 +----------------- 4 files changed, 770 insertions(+), 749 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js create mode 100644 modules/ppcp-blocks/resources/js/Components/paypal-label.js create mode 100644 modules/ppcp-blocks/resources/js/Components/paypal.js diff --git a/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js b/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js new file mode 100644 index 000000000..37d99539c --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js @@ -0,0 +1,52 @@ +import { useMemo } from '@wordpress/element'; +import { normalizeStyleForFundingSource } from '../../../../ppcp-button/resources/js/modules/Helper/Style'; +import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js'; + +export const BlockEditorPayPalComponent = ( { + config, + fundingSource, + buttonAttributes, +} ) => { + const urlParams = useMemo( + () => ( { + clientId: 'test', + ...config.scriptData.url_params, + dataNamespace: 'ppcp-blocks-editor-paypal-buttons', + components: 'buttons', + } ), + [] + ); + + const style = useMemo( () => { + const configStyle = normalizeStyleForFundingSource( + config.scriptData.button.style, + fundingSource + ); + + if ( buttonAttributes ) { + return { + ...configStyle, + height: buttonAttributes.height + ? Number( buttonAttributes.height ) + : configStyle.height, + borderRadius: buttonAttributes.borderRadius + ? Number( buttonAttributes.borderRadius ) + : configStyle.borderRadius, + }; + } + + return configStyle; + }, [ fundingSource, buttonAttributes ] ); + + return ( + + false } + /> + + ); +}; diff --git a/modules/ppcp-blocks/resources/js/Components/paypal-label.js b/modules/ppcp-blocks/resources/js/Components/paypal-label.js new file mode 100644 index 000000000..0cb9eb95d --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/paypal-label.js @@ -0,0 +1,14 @@ +export const PaypalLabel = ( { components, config } ) => { + const { PaymentMethodIcons } = components; + + return ( + <> + + + + ); +}; diff --git a/modules/ppcp-blocks/resources/js/Components/paypal.js b/modules/ppcp-blocks/resources/js/Components/paypal.js new file mode 100644 index 000000000..577fe3914 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/paypal.js @@ -0,0 +1,687 @@ +import { useEffect, useState } from '@wordpress/element'; +import { loadPayPalScript } from '../../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading'; +import { + mergeWcAddress, + paypalAddressToWc, + paypalOrderToWcAddresses, + paypalSubscriptionToWcAddresses, +} from '../Helper/Address'; +import { convertKeysToSnakeCase } from '../Helper/Helper'; +import buttonModuleWatcher from '../../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; +import { normalizeStyleForFundingSource } from '../../../../ppcp-button/resources/js/modules/Helper/Style'; +import { isPayPalSubscription } from '../Helper/Subscription'; + +const PAYPAL_GATEWAY_ID = 'ppcp-gateway'; + +const namespace = 'ppcpBlocksPaypalExpressButtons'; +let registeredContext = false; +let paypalScriptPromise = null; + +export const PayPalComponent = ( { + config, + onClick, + onClose, + onSubmit, + onError, + eventRegistration, + emitResponse, + activePaymentMethod, + shippingData, + isEditing, + fundingSource, + buttonAttributes, +} ) => { + const { onPaymentSetup, onCheckoutFail, onCheckoutValidation } = + eventRegistration; + const { responseTypes } = emitResponse; + + const [ paypalOrder, setPaypalOrder ] = useState( null ); + const [ continuationFilled, setContinuationFilled ] = useState( false ); + const [ gotoContinuationOnError, setGotoContinuationOnError ] = + useState( false ); + + const [ paypalScriptLoaded, setPaypalScriptLoaded ] = useState( false ); + + if ( ! paypalScriptLoaded ) { + if ( ! paypalScriptPromise ) { + // for editor, since canMakePayment was not called + paypalScriptPromise = loadPayPalScript( + namespace, + config.scriptData + ); + } + paypalScriptPromise.then( () => setPaypalScriptLoaded( true ) ); + } + + const methodId = fundingSource + ? `${ config.id }-${ fundingSource }` + : config.id; + + /** + * The block cart displays express checkout buttons. Those buttons are handled by the + * PAYPAL_GATEWAY_ID method on the server ("PayPal Smart Buttons"). + * + * A possible bug in WooCommerce does not use the correct payment method ID for the express + * payment buttons inside the cart, but sends the ID of the _first_ active payment method. + * + * This function uses an internal WooCommerce dispatcher method to set the correct method ID. + */ + const enforcePaymentMethodForCart = () => { + // Do nothing, unless we're handling block cart express payment buttons. + if ( 'cart-block' !== config.scriptData.context ) { + return; + } + + // Set the active payment method to PAYPAL_GATEWAY_ID. + wp.data + .dispatch( 'wc/store/payment' ) + .__internalSetActivePaymentMethod( PAYPAL_GATEWAY_ID, {} ); + }; + + useEffect( () => { + // fill the form if in continuation (for product or mini-cart buttons) + if ( continuationFilled || ! config.scriptData.continuation?.order ) { + return; + } + + try { + const paypalAddresses = paypalOrderToWcAddresses( + config.scriptData.continuation.order + ); + const wcAddresses = wp.data + .select( 'wc/store/cart' ) + .getCustomerData(); + const addresses = mergeWcAddress( wcAddresses, paypalAddresses ); + + wp.data + .dispatch( 'wc/store/cart' ) + .setBillingAddress( addresses.billingAddress ); + + if ( shippingData.needsShipping ) { + wp.data + .dispatch( 'wc/store/cart' ) + .setShippingAddress( addresses.shippingAddress ); + } + } catch ( err ) { + // sometimes the PayPal address is missing, skip in this case. + console.error( err ); + } + + // this useEffect should run only once, but adding this in case of some kind of full re-rendering + setContinuationFilled( true ); + }, [ shippingData, continuationFilled ] ); + + const createOrder = async ( data ) => { + try { + const requestBody = { + nonce: config.scriptData.ajax.create_order.nonce, + bn_code: '', + context: config.scriptData.context, + payment_method: 'ppcp-gateway', + funding_source: window.ppcpFundingSource ?? 'paypal', + createaccount: false, + ...( data?.paymentSource && { + payment_source: data.paymentSource, + } ), + }; + + const res = await fetch( + config.scriptData.ajax.create_order.endpoint, + { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( requestBody ), + } + ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( json.data?.details?.length > 0 ) { + throw new Error( + json.data.details + .map( ( d ) => `${ d.issue } ${ d.description }` ) + .join( '
' ) + ); + } else if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + return json.data.id; + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } + }; + + const createSubscription = async ( data, actions ) => { + let planId = config.scriptData.subscription_plan_id; + if ( + config.scriptData + .variable_paypal_subscription_variation_from_cart !== '' + ) { + planId = + config.scriptData + .variable_paypal_subscription_variation_from_cart; + } + + return actions.subscription.create( { + plan_id: planId, + } ); + }; + + const handleApproveSubscription = async ( data, actions ) => { + try { + const subscription = await actions.subscription.get(); + + if ( subscription ) { + const addresses = + paypalSubscriptionToWcAddresses( subscription ); + + const promises = [ + // save address on server + wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + billing_address: addresses.billingAddress, + shipping_address: addresses.shippingAddress, + } ), + ]; + if ( shouldHandleShippingInPayPal() ) { + // set address in UI + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setBillingAddress( addresses.billingAddress ) + ); + if ( shippingData.needsShipping ) { + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setShippingAddress( addresses.shippingAddress ) + ); + } + } + await Promise.all( promises ); + } + + setPaypalOrder( subscription ); + + const res = await fetch( + config.scriptData.ajax.approve_subscription.endpoint, + { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.scriptData.ajax.approve_subscription + .nonce, + order_id: data.orderID, + subscription_id: data.subscriptionID, + } ), + } + ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( + typeof actions !== 'undefined' && + typeof actions.restart !== 'undefined' + ) { + return actions.restart(); + } + if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + + if ( ! shouldskipFinalConfirmation() ) { + location.href = getCheckoutRedirectUrl(); + } else { + setGotoContinuationOnError( true ); + enforcePaymentMethodForCart(); + onSubmit(); + } + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } + }; + + const getCheckoutRedirectUrl = () => { + const checkoutUrl = new URL( config.scriptData.redirect ); + // sometimes some browsers may load some kind of cached version of the page, + // so adding a parameter to avoid that + checkoutUrl.searchParams.append( + 'ppcp-continuation-redirect', + new Date().getTime().toString() + ); + return checkoutUrl.toString(); + }; + + const handleApprove = async ( data, actions ) => { + try { + const order = await actions.order.get(); + + if ( order ) { + const addresses = paypalOrderToWcAddresses( order ); + + const promises = [ + // save address on server + wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + billing_address: addresses.billingAddress, + shipping_address: addresses.shippingAddress, + } ), + ]; + if ( shouldHandleShippingInPayPal() ) { + // set address in UI + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setBillingAddress( addresses.billingAddress ) + ); + if ( shippingData.needsShipping ) { + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setShippingAddress( addresses.shippingAddress ) + ); + } + } + await Promise.all( promises ); + } + + setPaypalOrder( order ); + + const res = await fetch( + config.scriptData.ajax.approve_order.endpoint, + { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.scriptData.ajax.approve_order.nonce, + order_id: data.orderID, + funding_source: window.ppcpFundingSource ?? 'paypal', + } ), + } + ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( + typeof actions !== 'undefined' && + typeof actions.restart !== 'undefined' + ) { + return actions.restart(); + } + if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + + if ( ! shouldskipFinalConfirmation() ) { + location.href = getCheckoutRedirectUrl(); + } else { + setGotoContinuationOnError( true ); + enforcePaymentMethodForCart(); + onSubmit(); + } + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } + }; + + useEffect( () => { + const unsubscribe = onCheckoutValidation( () => { + if ( config.scriptData.continuation ) { + return true; + } + if ( + gotoContinuationOnError && + wp.data.select( 'wc/store/validation' ).hasValidationErrors() + ) { + location.href = getCheckoutRedirectUrl(); + return { type: responseTypes.ERROR }; + } + + return true; + } ); + return unsubscribe; + }, [ onCheckoutValidation, gotoContinuationOnError ] ); + + const handleClick = ( data, actions ) => { + if ( isEditing ) { + return actions.reject(); + } + + window.ppcpFundingSource = data.fundingSource; + + onClick(); + }; + + const shouldHandleShippingInPayPal = () => { + return shouldskipFinalConfirmation() && config.needShipping; + }; + + const shouldskipFinalConfirmation = () => { + if ( config.finalReviewEnabled ) { + return false; + } + + return ( + window.ppcpFundingSource !== 'venmo' || + ! config.scriptData.vaultingEnabled + ); + }; + + let handleShippingOptionsChange = null; + let handleShippingAddressChange = null; + let handleSubscriptionShippingOptionsChange = null; + let handleSubscriptionShippingAddressChange = null; + + if ( shippingData.needsShipping && shouldHandleShippingInPayPal() ) { + handleShippingOptionsChange = async ( data, actions ) => { + try { + const shippingOptionId = data.selectedShippingOption?.id; + if ( shippingOptionId ) { + await wp.data + .dispatch( 'wc/store/cart' ) + .selectShippingRate( shippingOptionId ); + await shippingData.setSelectedRates( shippingOptionId ); + } + + const res = await fetch( config.ajax.update_shipping.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.ajax.update_shipping.nonce, + order_id: data.orderID, + } ), + } ); + + const json = await res.json(); + + if ( ! json.success ) { + throw new Error( json.data.message ); + } + } catch ( e ) { + console.error( e ); + + actions.reject(); + } + }; + + handleShippingAddressChange = async ( data, actions ) => { + try { + const address = paypalAddressToWc( + convertKeysToSnakeCase( data.shippingAddress ) + ); + + await wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + shipping_address: address, + } ); + + await shippingData.setShippingAddress( address ); + + const res = await fetch( config.ajax.update_shipping.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.ajax.update_shipping.nonce, + order_id: data.orderID, + } ), + } ); + + const json = await res.json(); + + if ( ! json.success ) { + throw new Error( json.data.message ); + } + } catch ( e ) { + console.error( e ); + + actions.reject(); + } + }; + + handleSubscriptionShippingOptionsChange = async ( data, actions ) => { + try { + const shippingOptionId = data.selectedShippingOption?.id; + if ( shippingOptionId ) { + await wp.data + .dispatch( 'wc/store/cart' ) + .selectShippingRate( shippingOptionId ); + await shippingData.setSelectedRates( shippingOptionId ); + } + } catch ( e ) { + console.error( e ); + + actions.reject(); + } + }; + + handleSubscriptionShippingAddressChange = async ( data, actions ) => { + try { + const address = paypalAddressToWc( + convertKeysToSnakeCase( data.shippingAddress ) + ); + + await wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + shipping_address: address, + } ); + + await shippingData.setShippingAddress( address ); + + const res = await fetch( config.ajax.update_shipping.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.ajax.update_shipping.nonce, + order_id: data.orderID, + } ), + } ); + + const json = await res.json(); + + if ( ! json.success ) { + throw new Error( json.data.message ); + } + } catch ( e ) { + console.error( e ); + + actions.reject(); + } + }; + } + + useEffect( () => { + if ( activePaymentMethod !== methodId ) { + return; + } + + const unsubscribeProcessing = onPaymentSetup( () => { + if ( config.scriptData.continuation ) { + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + paypal_order_id: + config.scriptData.continuation.order_id, + funding_source: + window.ppcpFundingSource ?? 'paypal', + }, + }, + }; + } + + const addresses = paypalOrderToWcAddresses( paypalOrder ); + + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + paypal_order_id: paypalOrder.id, + funding_source: window.ppcpFundingSource ?? 'paypal', + }, + ...addresses, + }, + }; + } ); + return () => { + unsubscribeProcessing(); + }; + }, [ onPaymentSetup, paypalOrder, activePaymentMethod ] ); + + useEffect( () => { + if ( activePaymentMethod !== methodId ) { + return; + } + const unsubscribe = onCheckoutFail( ( { processingResponse } ) => { + console.error( processingResponse ); + if ( onClose ) { + onClose(); + } + if ( config.scriptData.continuation ) { + return true; + } + if ( shouldskipFinalConfirmation() ) { + location.href = getCheckoutRedirectUrl(); + } + return true; + } ); + return unsubscribe; + }, [ onCheckoutFail, onClose, activePaymentMethod ] ); + + if ( config.scriptData.continuation ) { + return ( +
+ ); + } + + if ( ! registeredContext ) { + buttonModuleWatcher.registerContextBootstrap( + config.scriptData.context, + { + createOrder: () => { + return createOrder(); + }, + onApprove: ( data, actions ) => { + return handleApprove( data, actions ); + }, + } + ); + registeredContext = true; + } + + const style = normalizeStyleForFundingSource( + config.scriptData.button.style, + fundingSource + ); + + if ( typeof buttonAttributes !== 'undefined' ) { + style.height = buttonAttributes?.height + ? Number( buttonAttributes.height ) + : style.height; + style.borderRadius = buttonAttributes?.borderRadius + ? Number( buttonAttributes.borderRadius ) + : style.borderRadius; + } + + if ( ! paypalScriptLoaded ) { + return null; + } + + const PayPalButton = ppcpBlocksPaypalExpressButtons.Buttons.driver( + 'react', + { React, ReactDOM } + ); + + const getOnShippingOptionsChange = ( fundingSource ) => { + if ( fundingSource === 'venmo' ) { + return null; + } + + return ( data, actions ) => { + shouldHandleShippingInPayPal() + ? handleShippingOptionsChange( data, actions ) + : null; + }; + }; + + const getOnShippingAddressChange = ( fundingSource ) => { + if ( fundingSource === 'venmo' ) { + return null; + } + + return ( data, actions ) => { + const shippingAddressChange = shouldHandleShippingInPayPal() + ? handleShippingAddressChange( data, actions ) + : null; + + return shippingAddressChange; + }; + }; + + if ( isPayPalSubscription( config.scriptData ) ) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index b0f971dd2..c02015833 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -1,750 +1,26 @@ -import { useEffect, useState, useMemo } from '@wordpress/element'; import { registerExpressPaymentMethod, registerPaymentMethod, } from '@woocommerce/blocks-registry'; import { __ } from '@wordpress/i18n'; -import { - mergeWcAddress, - paypalAddressToWc, - paypalOrderToWcAddresses, - paypalSubscriptionToWcAddresses, -} from './Helper/Address'; -import { convertKeysToSnakeCase } from './Helper/Helper'; import { cartHasSubscriptionProducts, isPayPalSubscription, } from './Helper/Subscription'; import { loadPayPalScript } from '../../../ppcp-button/resources/js/modules/Helper/PayPalScriptLoading'; -import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js'; -import { normalizeStyleForFundingSource } from '../../../ppcp-button/resources/js/modules/Helper/Style'; -import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; import BlockCheckoutMessagesBootstrap from './Bootstrap/BlockCheckoutMessagesBootstrap'; +import { PayPalComponent } from './Components/paypal'; +import { BlockEditorPayPalComponent } from './Components/block-editor-paypal'; +import { PaypalLabel } from './Components/paypal-label'; const namespace = 'ppcpBlocksPaypalExpressButtons'; const config = wc.wcSettings.getSetting( 'ppcp-gateway_data' ); window.ppcpFundingSource = config.fundingSource; -let registeredContext = false; let paypalScriptPromise = null; -const PAYPAL_GATEWAY_ID = 'ppcp-gateway'; - -const PayPalComponent = ( { - onClick, - onClose, - onSubmit, - onError, - eventRegistration, - emitResponse, - activePaymentMethod, - shippingData, - isEditing, - fundingSource, - buttonAttributes, -} ) => { - const { onPaymentSetup, onCheckoutFail, onCheckoutValidation } = - eventRegistration; - const { responseTypes } = emitResponse; - - const [ paypalOrder, setPaypalOrder ] = useState( null ); - const [ continuationFilled, setContinuationFilled ] = useState( false ); - const [ gotoContinuationOnError, setGotoContinuationOnError ] = - useState( false ); - - const [ paypalScriptLoaded, setPaypalScriptLoaded ] = useState( false ); - - if ( ! paypalScriptLoaded ) { - if ( ! paypalScriptPromise ) { - // for editor, since canMakePayment was not called - paypalScriptPromise = loadPayPalScript( - namespace, - config.scriptData - ); - } - paypalScriptPromise.then( () => setPaypalScriptLoaded( true ) ); - } - - const methodId = fundingSource - ? `${ config.id }-${ fundingSource }` - : config.id; - - /** - * The block cart displays express checkout buttons. Those buttons are handled by the - * PAYPAL_GATEWAY_ID method on the server ("PayPal Smart Buttons"). - * - * A possible bug in WooCommerce does not use the correct payment method ID for the express - * payment buttons inside the cart, but sends the ID of the _first_ active payment method. - * - * This function uses an internal WooCommerce dispatcher method to set the correct method ID. - */ - const enforcePaymentMethodForCart = () => { - // Do nothing, unless we're handling block cart express payment buttons. - if ( 'cart-block' !== config.scriptData.context ) { - return; - } - - // Set the active payment method to PAYPAL_GATEWAY_ID. - wp.data - .dispatch( 'wc/store/payment' ) - .__internalSetActivePaymentMethod( PAYPAL_GATEWAY_ID, {} ); - }; - - useEffect( () => { - // fill the form if in continuation (for product or mini-cart buttons) - if ( continuationFilled || ! config.scriptData.continuation?.order ) { - return; - } - - try { - const paypalAddresses = paypalOrderToWcAddresses( - config.scriptData.continuation.order - ); - const wcAddresses = wp.data - .select( 'wc/store/cart' ) - .getCustomerData(); - const addresses = mergeWcAddress( wcAddresses, paypalAddresses ); - - wp.data - .dispatch( 'wc/store/cart' ) - .setBillingAddress( addresses.billingAddress ); - - if ( shippingData.needsShipping ) { - wp.data - .dispatch( 'wc/store/cart' ) - .setShippingAddress( addresses.shippingAddress ); - } - } catch ( err ) { - // sometimes the PayPal address is missing, skip in this case. - console.log( err ); - } - - // this useEffect should run only once, but adding this in case of some kind of full re-rendering - setContinuationFilled( true ); - }, [ shippingData, continuationFilled ] ); - - const createOrder = async ( data, actions ) => { - try { - const requestBody = { - nonce: config.scriptData.ajax.create_order.nonce, - bn_code: '', - context: config.scriptData.context, - payment_method: 'ppcp-gateway', - funding_source: window.ppcpFundingSource ?? 'paypal', - createaccount: false, - ...( data?.paymentSource && { - payment_source: data.paymentSource, - } ), - }; - - const res = await fetch( - config.scriptData.ajax.create_order.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( requestBody ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( json.data?.details?.length > 0 ) { - throw new Error( - json.data.details - .map( ( d ) => `${ d.issue } ${ d.description }` ) - .join( '
' ) - ); - } else if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - return json.data.id; - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - - const createSubscription = async ( data, actions ) => { - let planId = config.scriptData.subscription_plan_id; - if ( - config.scriptData - .variable_paypal_subscription_variation_from_cart !== '' - ) { - planId = - config.scriptData - .variable_paypal_subscription_variation_from_cart; - } - - return actions.subscription.create( { - plan_id: planId, - } ); - }; - - const handleApproveSubscription = async ( data, actions ) => { - try { - const subscription = await actions.subscription.get(); - - if ( subscription ) { - const addresses = - paypalSubscriptionToWcAddresses( subscription ); - - const promises = [ - // save address on server - wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - billing_address: addresses.billingAddress, - shipping_address: addresses.shippingAddress, - } ), - ]; - if ( shouldHandleShippingInPayPal() ) { - // set address in UI - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setBillingAddress( addresses.billingAddress ) - ); - if ( shippingData.needsShipping ) { - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setShippingAddress( addresses.shippingAddress ) - ); - } - } - await Promise.all( promises ); - } - - setPaypalOrder( subscription ); - - const res = await fetch( - config.scriptData.ajax.approve_subscription.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.scriptData.ajax.approve_subscription - .nonce, - order_id: data.orderID, - subscription_id: data.subscriptionID, - } ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( - typeof actions !== 'undefined' && - typeof actions.restart !== 'undefined' - ) { - return actions.restart(); - } - if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - - if ( ! shouldskipFinalConfirmation() ) { - location.href = getCheckoutRedirectUrl(); - } else { - setGotoContinuationOnError( true ); - enforcePaymentMethodForCart(); - onSubmit(); - } - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - - const getCheckoutRedirectUrl = () => { - const checkoutUrl = new URL( config.scriptData.redirect ); - // sometimes some browsers may load some kind of cached version of the page, - // so adding a parameter to avoid that - checkoutUrl.searchParams.append( - 'ppcp-continuation-redirect', - new Date().getTime().toString() - ); - return checkoutUrl.toString(); - }; - - const handleApprove = async ( data, actions ) => { - try { - const order = await actions.order.get(); - - if ( order ) { - const addresses = paypalOrderToWcAddresses( order ); - - const promises = [ - // save address on server - wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - billing_address: addresses.billingAddress, - shipping_address: addresses.shippingAddress, - } ), - ]; - if ( shouldHandleShippingInPayPal() ) { - // set address in UI - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setBillingAddress( addresses.billingAddress ) - ); - if ( shippingData.needsShipping ) { - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setShippingAddress( addresses.shippingAddress ) - ); - } - } - await Promise.all( promises ); - } - - setPaypalOrder( order ); - - const res = await fetch( - config.scriptData.ajax.approve_order.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.scriptData.ajax.approve_order.nonce, - order_id: data.orderID, - funding_source: window.ppcpFundingSource ?? 'paypal', - } ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( - typeof actions !== 'undefined' && - typeof actions.restart !== 'undefined' - ) { - return actions.restart(); - } - if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - - if ( ! shouldskipFinalConfirmation() ) { - location.href = getCheckoutRedirectUrl(); - } else { - setGotoContinuationOnError( true ); - enforcePaymentMethodForCart(); - onSubmit(); - } - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - - useEffect( () => { - const unsubscribe = onCheckoutValidation( () => { - if ( config.scriptData.continuation ) { - return true; - } - if ( - gotoContinuationOnError && - wp.data.select( 'wc/store/validation' ).hasValidationErrors() - ) { - location.href = getCheckoutRedirectUrl(); - return { type: responseTypes.ERROR }; - } - - return true; - } ); - return unsubscribe; - }, [ onCheckoutValidation, gotoContinuationOnError ] ); - - const handleClick = ( data, actions ) => { - if ( isEditing ) { - return actions.reject(); - } - - window.ppcpFundingSource = data.fundingSource; - - onClick(); - }; - - const shouldHandleShippingInPayPal = () => { - return shouldskipFinalConfirmation() && config.needShipping; - }; - - const shouldskipFinalConfirmation = () => { - if ( config.finalReviewEnabled ) { - return false; - } - - return ( - window.ppcpFundingSource !== 'venmo' || - ! config.scriptData.vaultingEnabled - ); - }; - - let handleShippingOptionsChange = null; - let handleShippingAddressChange = null; - let handleSubscriptionShippingOptionsChange = null; - let handleSubscriptionShippingAddressChange = null; - - if ( shippingData.needsShipping && shouldHandleShippingInPayPal() ) { - handleShippingOptionsChange = async ( data, actions ) => { - try { - const shippingOptionId = data.selectedShippingOption?.id; - if ( shippingOptionId ) { - await wp.data - .dispatch( 'wc/store/cart' ) - .selectShippingRate( shippingOptionId ); - await shippingData.setSelectedRates( shippingOptionId ); - } - - const res = await fetch( config.ajax.update_shipping.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.ajax.update_shipping.nonce, - order_id: data.orderID, - } ), - } ); - - const json = await res.json(); - - if ( ! json.success ) { - throw new Error( json.data.message ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; - - handleShippingAddressChange = async ( data, actions ) => { - try { - const address = paypalAddressToWc( - convertKeysToSnakeCase( data.shippingAddress ) - ); - - await wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - shipping_address: address, - } ); - - await shippingData.setShippingAddress( address ); - - const res = await fetch( config.ajax.update_shipping.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.ajax.update_shipping.nonce, - order_id: data.orderID, - } ), - } ); - - const json = await res.json(); - - if ( ! json.success ) { - throw new Error( json.data.message ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; - - handleSubscriptionShippingOptionsChange = async ( data, actions ) => { - try { - const shippingOptionId = data.selectedShippingOption?.id; - if ( shippingOptionId ) { - await wp.data - .dispatch( 'wc/store/cart' ) - .selectShippingRate( shippingOptionId ); - await shippingData.setSelectedRates( shippingOptionId ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; - - handleSubscriptionShippingAddressChange = async ( data, actions ) => { - try { - const address = paypalAddressToWc( - convertKeysToSnakeCase( data.shippingAddress ) - ); - - await wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - shipping_address: address, - } ); - - await shippingData.setShippingAddress( address ); - - const res = await fetch( config.ajax.update_shipping.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.ajax.update_shipping.nonce, - order_id: data.orderID, - } ), - } ); - - const json = await res.json(); - - if ( ! json.success ) { - throw new Error( json.data.message ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; - } - - useEffect( () => { - if ( activePaymentMethod !== methodId ) { - return; - } - - const unsubscribeProcessing = onPaymentSetup( () => { - if ( config.scriptData.continuation ) { - return { - type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - paypal_order_id: - config.scriptData.continuation.order_id, - funding_source: - window.ppcpFundingSource ?? 'paypal', - }, - }, - }; - } - - const addresses = paypalOrderToWcAddresses( paypalOrder ); - - return { - type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - paypal_order_id: paypalOrder.id, - funding_source: window.ppcpFundingSource ?? 'paypal', - }, - ...addresses, - }, - }; - } ); - return () => { - unsubscribeProcessing(); - }; - }, [ onPaymentSetup, paypalOrder, activePaymentMethod ] ); - - useEffect( () => { - if ( activePaymentMethod !== methodId ) { - return; - } - const unsubscribe = onCheckoutFail( ( { processingResponse } ) => { - console.error( processingResponse ); - if ( onClose ) { - onClose(); - } - if ( config.scriptData.continuation ) { - return true; - } - if ( shouldskipFinalConfirmation() ) { - location.href = getCheckoutRedirectUrl(); - } - return true; - } ); - return unsubscribe; - }, [ onCheckoutFail, onClose, activePaymentMethod ] ); - - if ( config.scriptData.continuation ) { - return ( -
- ); - } - - if ( ! registeredContext ) { - buttonModuleWatcher.registerContextBootstrap( - config.scriptData.context, - { - createOrder: () => { - return createOrder(); - }, - onApprove: ( data, actions ) => { - return handleApprove( data, actions ); - }, - } - ); - registeredContext = true; - } - - const style = normalizeStyleForFundingSource( - config.scriptData.button.style, - fundingSource - ); - - if ( typeof buttonAttributes !== 'undefined' ) { - style.height = buttonAttributes?.height - ? Number( buttonAttributes.height ) - : style.height; - style.borderRadius = buttonAttributes?.borderRadius - ? Number( buttonAttributes.borderRadius ) - : style.borderRadius; - } - - if ( ! paypalScriptLoaded ) { - return null; - } - - const PayPalButton = ppcpBlocksPaypalExpressButtons.Buttons.driver( - 'react', - { React, ReactDOM } - ); - - const getOnShippingOptionsChange = ( fundingSource ) => { - if ( fundingSource === 'venmo' ) { - return null; - } - - return ( data, actions ) => { - shouldHandleShippingInPayPal() - ? handleShippingOptionsChange( data, actions ) - : null; - }; - }; - - const getOnShippingAddressChange = ( fundingSource ) => { - if ( fundingSource === 'venmo' ) { - return null; - } - - return ( data, actions ) => { - const shippingAddressChange = shouldHandleShippingInPayPal() - ? handleShippingAddressChange( data, actions ) - : null; - - return shippingAddressChange; - }; - }; - - if ( isPayPalSubscription( config.scriptData ) ) { - return ( - - ); - } - - return ( - - ); -}; - -const BlockEditorPayPalComponent = ( { fundingSource, buttonAttributes } ) => { - const urlParams = useMemo( - () => ( { - clientId: 'test', - ...config.scriptData.url_params, - dataNamespace: 'ppcp-blocks-editor-paypal-buttons', - components: 'buttons', - } ), - [] - ); - - const style = useMemo( () => { - const configStyle = normalizeStyleForFundingSource( - config.scriptData.button.style, - fundingSource - ); - - if ( buttonAttributes ) { - return { - ...configStyle, - height: buttonAttributes.height - ? Number( buttonAttributes.height ) - : configStyle.height, - borderRadius: buttonAttributes.borderRadius - ? Number( buttonAttributes.borderRadius ) - : configStyle.borderRadius, - }; - } - - return configStyle; - }, [ fundingSource, buttonAttributes ] ); - - return ( - - false } - /> - - ); -}; - const features = [ 'products' ]; -let block_enabled = true; +let blockEnabled = true; if ( cartHasSubscriptionProducts( config.scriptData ) ) { // Don't show buttons on block cart page if using vault v2 and user is not logged in @@ -754,7 +30,7 @@ if ( cartHasSubscriptionProducts( config.scriptData ) ) { ! isPayPalSubscription( config.scriptData ) && // using vaulting ! config.scriptData?.save_payment_methods?.id_token // not vault v3 ) { - block_enabled = false; + blockEnabled = false; } // Don't render if vaulting disabled and is in vault subscription mode @@ -762,7 +38,7 @@ if ( cartHasSubscriptionProducts( config.scriptData ) ) { ! isPayPalSubscription( config.scriptData ) && ! config.scriptData.can_save_vault_token ) { - block_enabled = false; + blockEnabled = false; } // Don't render buttons if in subscription mode and product not associated with a PayPal subscription @@ -770,13 +46,13 @@ if ( cartHasSubscriptionProducts( config.scriptData ) ) { isPayPalSubscription( config.scriptData ) && ! config.scriptData.subscription_product_allowed ) { - block_enabled = false; + blockEnabled = false; } features.push( 'subscriptions' ); } -if ( block_enabled ) { +if ( blockEnabled ) { if ( config.placeOrderEnabled && ! config.scriptData.continuation ) { let descriptionElement = (
{ - const { PaymentMethodIcons } = components; - - return ( - <> - - - - ); - }; - registerPaymentMethod( { name: config.id, label: , @@ -837,8 +98,13 @@ if ( block_enabled ) { registerPaymentMethod( { name: config.id, label:
, - content: , - edit: , + content: , + edit: ( + + ), ariaLabel: config.title, canMakePayment: () => { return true; @@ -866,12 +132,14 @@ if ( block_enabled ) { ), content: ( ), edit: ( ), From 10a95b0f2e797b484d7c67896bcf1aea6b4dad42 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 3 Dec 2024 16:25:14 +0400 Subject: [PATCH 006/169] Check for list ID existance inside the task registrar --- .../src/Settings/WcTasks/Registrar/TaskRegistrar.php | 9 +++++++-- .../WcTasks/Registrar/TaskRegistrarInterface.php | 5 +++-- modules/ppcp-wc-gateway/src/WCGatewayModule.php | 8 +------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrar.php b/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrar.php index 174e60e6b..a65443b80 100644 --- a/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrar.php +++ b/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrar.php @@ -21,9 +21,14 @@ class TaskRegistrar implements TaskRegistrarInterface { * * @throws RuntimeException If problem registering. */ - public function register( array $tasks ): void { + public function register( string $list_id, array $tasks ): void { + $task_lists = TaskLists::get_lists(); + if ( ! isset( $task_lists[ $list_id ] ) ) { + return; + } + foreach ( $tasks as $task ) { - $added_task = TaskLists::add_task( 'extended', $task ); + $added_task = TaskLists::add_task( $list_id, $task ); if ( $added_task instanceof WP_Error ) { throw new RuntimeException( $added_task->get_error_message() ); } diff --git a/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrarInterface.php b/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrarInterface.php index 992b41150..0f8ee555d 100644 --- a/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrarInterface.php +++ b/modules/ppcp-wc-gateway/src/Settings/WcTasks/Registrar/TaskRegistrarInterface.php @@ -15,11 +15,12 @@ use RuntimeException; interface TaskRegistrarInterface { /** - * Registers the tasks inside "Things to do next" WC section. + * Registers the tasks inside the section with given list ID. * + * @param string $list_id The list ID. * @param Task[] $tasks The list of tasks. * @return void * @throws RuntimeException If problem registering. */ - public function register( array $tasks ): void; + public function register( string $list_id, array $tasks ): void; } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 4ca10c92f..1f644a6a6 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -9,7 +9,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway; -use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; use Exception; use Psr\Log\LoggerInterface; use Throwable; @@ -868,11 +867,6 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $logger = $container->get( 'woocommerce.logger.woocommerce' ); assert( $logger instanceof LoggerInterface ); try { - $task_lists = TaskLists::get_lists(); - if ( ! isset( $task_lists['extended'] ) ) { - return; - } - $simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' ); if ( empty( $simple_redirect_tasks ) ) { return; @@ -881,7 +875,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul $task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' ); assert( $task_registrar instanceof TaskRegistrarInterface ); - $task_registrar->register( $simple_redirect_tasks ); + $task_registrar->register( 'extended', $simple_redirect_tasks ); } catch ( Exception $exception ) { $logger->error( "Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage() ); } From fbbec10ea31987e3c91b3ba43f0fc48a66ae9574 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 3 Dec 2024 18:06:23 +0400 Subject: [PATCH 007/169] 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 16:13:36 +0100 Subject: [PATCH 008/169] Add save payment button configuration (WIP) --- .../resources/js/Components/card-fields.js | 54 ++++++++----- .../resources/js/Components/paypal.js | 77 ++++++++++++++++++- 2 files changed, 111 insertions(+), 20 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index f47b18f35..30de01bb1 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -3,10 +3,10 @@ import { useEffect, useState } from '@wordpress/element'; import { PayPalScriptProvider, PayPalCardFieldsProvider, - PayPalNameField, - PayPalNumberField, - PayPalExpiryField, - PayPalCVVField, + PayPalNameField, + PayPalNumberField, + PayPalExpiryField, + PayPalCVVField, } from '@paypal/react-paypal-js'; import { CheckoutHandler } from './checkout-handler'; @@ -19,11 +19,7 @@ import { import { cartHasSubscriptionProducts } from '../Helper/Subscription'; import { __ } from '@wordpress/i18n'; -export function CardFields( { - config, - eventRegistration, - emitResponse, -} ) { +export function CardFields( { config, eventRegistration, emitResponse } ) { const { onPaymentSetup } = eventRegistration; const { responseTypes } = emitResponse; @@ -96,16 +92,36 @@ export function CardFields( { console.error( err ); } } > - - -
-
- -
-
- -
-
+ + +
+
+ +
+
+ +
+
{ + return fetch( config.scriptData.ajax.create_setup_token.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( { + nonce: config.scriptData.ajax.create_setup_token.nonce, + payment_method: 'ppcp-gateway', + } ), + } ) + .then( ( response ) => response.json() ) + .then( ( result ) => { + return result.data.id; + } ) + .catch( ( err ) => { + console.error( err ); + } ); + }; + + const onApproveSavePayment = async ( { vaultSetupToken } ) => { + let endpoint = + config.scriptData.ajax.create_payment_token_for_guest.endpoint; + let bodyContent = { + nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce, + vault_setup_token: vaultSetupToken, + }; + + if ( config.scriptData.user.is_logged_in ) { + endpoint = config.scriptData.ajax.create_payment_token.endpoint; + + bodyContent = { + nonce: config.scriptData.ajax.create_payment_token.nonce, + vault_setup_token: vaultSetupToken, + is_free_trial_cart: config.scriptData.is_free_trial_cart, + }; + } + + const response = await fetch( endpoint, { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( bodyContent ), + } ); + + const result = await response.json(); + if ( result.success === true ) { + onSubmit(); + } + + console.error( result ); + }; + + if ( + cartHasSubscriptionProducts( config.scriptData ) && + config.scriptData.is_free_trial_cart + ) { + return ( + + ); + } + if ( isPayPalSubscription( config.scriptData ) ) { return ( Date: Tue, 3 Dec 2024 16:29:37 +0100 Subject: [PATCH 009/169] Add save payment button configuration (WIP) --- modules/ppcp-blocks/resources/js/Components/paypal.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/ppcp-blocks/resources/js/Components/paypal.js b/modules/ppcp-blocks/resources/js/Components/paypal.js index 0daee0f19..b65a22c58 100644 --- a/modules/ppcp-blocks/resources/js/Components/paypal.js +++ b/modules/ppcp-blocks/resources/js/Components/paypal.js @@ -525,6 +525,15 @@ export const PayPalComponent = ( { } const unsubscribeProcessing = onPaymentSetup( () => { + if ( + cartHasSubscriptionProducts( config.scriptData ) && + config.scriptData.is_free_trial_cart + ) { + return { + type: responseTypes.SUCCESS, + }; + } + if ( config.scriptData.continuation ) { return { type: responseTypes.SUCCESS, From eac06cf045ce115482278468d88793770627be21 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 3 Dec 2024 17:30:36 +0100 Subject: [PATCH 010/169] Ensure `customer_id` is string --- .../src/Endpoint/CreatePaymentToken.php | 2 +- .../ppcp-save-payment-methods/src/Endpoint/CreateSetupToken.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php index 434a08925..e5235eb17 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php @@ -96,7 +96,7 @@ class CreatePaymentToken implements EndpointInterface { $customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); - $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source, $customer_id ); + $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source, (string)$customer_id ); if ( is_user_logged_in() && isset( $result->customer->id ) ) { $current_user_id = get_current_user_id(); diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreateSetupToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreateSetupToken.php index 6952feb43..0b53f0828 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreateSetupToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreateSetupToken.php @@ -105,7 +105,7 @@ class CreateSetupToken implements EndpointInterface { $customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); - $result = $this->payment_method_tokens_endpoint->setup_tokens( $payment_source, $customer_id ); + $result = $this->payment_method_tokens_endpoint->setup_tokens( $payment_source, (string) $customer_id ); wp_send_json_success( $result ); return true; From 4f0f71f5e09845692ffa88cb1a00999b86dcc4bf Mon Sep 17 00:00:00 2001 From: Himad M Date: Tue, 3 Dec 2024 15:24:49 -0400 Subject: [PATCH 011/169] 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 @@ class CommonSettings extends AbstractDataModel { * * @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 @@ class CommonRestEndpoint extends RestEndpoint { $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 012/169] =?UTF-8?q?=E2=9C=A8=20Add=20Redux=20defaults=20fo?= =?UTF-8?q?r=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 ee336293c1314ac962f71af30d0571f5cedd1eaf Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 4 Dec 2024 12:16:38 +0100 Subject: [PATCH 013/169] Do not display PayPal button for guest with free trial subscription --- modules/ppcp-blocks/resources/js/checkout-block.js | 9 +++++++++ .../src/Endpoint/CreatePaymentToken.php | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index c02015833..1aabdaca0 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -160,6 +160,15 @@ if ( blockEnabled ) { } await paypalScriptPromise; + if ( + ! config.scriptData.user.is_logged && + config.scriptData.context === 'cart-block' && + cartHasSubscriptionProducts( config.scriptData ) && + config.scriptData.is_free_trial_cart + ) { + return false; + } + return ppcpBlocksPaypalExpressButtons .Buttons( { fundingSource } ) .isEligible(); diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php index e5235eb17..b89d17e53 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php @@ -96,7 +96,7 @@ class CreatePaymentToken implements EndpointInterface { $customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); - $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source, (string)$customer_id ); + $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source, (string) $customer_id ); if ( is_user_logged_in() && isset( $result->customer->id ) ) { $current_user_id = get_current_user_id(); From 4d6bad1ffd3008a5d54350ed09880d39537bf8e1 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 4 Dec 2024 12:18:19 +0100 Subject: [PATCH 014/169] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20code=20style=20(?= =?UTF-8?q?comment=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 @@ class CommonSettings extends AbstractDataModel { } /** - * 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 @@ class CommonRestEndpoint extends RestEndpoint { * * @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 015/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20step-decisi?= =?UTF-8?q?on=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 { getSteps, getCurrentStep } 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 ]; -}; - 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 016/169] =?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 017/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20navi?= =?UTF-8?q?gation=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 018/169] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20cleanup=20of=20s?= =?UTF-8?q?css=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 019/169] =?UTF-8?q?=E2=9C=A8=20Consolidate=20navigation=20?= =?UTF-8?q?logic=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 020/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20real=20icon?= =?UTF-8?q?=20instead=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 022/169] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20SCSS=20for=20h?= =?UTF-8?q?eader=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; - button.is-primary { - padding: 10px 18px; - justify-content: center; - margin: 0 0 0 12px; - &:not(:disabled) { - background-color: $color-blueberry; + .components-button { + @include font(13, 20, 400); + + &.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); + } + } + + &.is-title { + @include font(16, 24, 600); + color: var(--color-text); + + .title { + margin-left: 18px; + } + + .big { + @include font(20, 28, 400); + } } } &--left { align-items: center; display: inline-flex; - - &__link { - @include font(20, 28, 400); - color: $color-gray-900; - text-decoration: none; - padding: 0 0 0 18px; - } - } - - &--right a{ - @include font(13, 20, 400); - color: $color-blueberry; - text-decoration: none; } &--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 023/169] =?UTF-8?q?=F0=9F=92=84=20Sticky=20nav=20bar,=20co?= =?UTF-8?q?nsolidate=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 | 21 ++++++++++++------- .../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, 35 insertions(+), 25 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; - margin: 0 -20px 48px -20px; - border-bottom: 1px solid $color-gray-300; - position: relative; + position: sticky; + top: var(--wp-admin--admin-bar--height); + z-index: 10; - --color-accent: #{$color-blueberry}; + padding: 10px 48px; + margin: 0 -20px 48px -20px; + + 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 024/169] =?UTF-8?q?=F0=9F=92=84=20Adjust=20paddings=20and?= =?UTF-8?q?=20border=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 025/169] =?UTF-8?q?=E2=9C=A8=20Add=20scroll-effect=20(shad?= =?UTF-8?q?ow)=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 031/169] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20setCom?= =?UTF-8?q?pleted=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 032/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor=20code=20imp?= =?UTF-8?q?rovement?= 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 033/169] =?UTF-8?q?=E2=9C=A8=20First=20draft=20of=20produc?= =?UTF-8?q?tion=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 034/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20Redux=20a?= =?UTF-8?q?ction=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 035/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20validat?= =?UTF-8?q?ion=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 036/169] =?UTF-8?q?=F0=9F=9A=B8=20Restore=20previous=20err?= =?UTF-8?q?or=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 037/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20erro?= =?UTF-8?q?r=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 038/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20label=20?= =?UTF-8?q?logic=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 039/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor=20code=20imp?= =?UTF-8?q?rovements?= 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 16:28:59 +0100 Subject: [PATCH 040/169] Extract actions from button components --- .../resources/js/Components/paypal.js | 365 ++++-------------- .../ppcp-blocks/resources/js/paypal-config.js | 316 +++++++++++++++ 2 files changed, 385 insertions(+), 296 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/paypal-config.js diff --git a/modules/ppcp-blocks/resources/js/Components/paypal.js b/modules/ppcp-blocks/resources/js/Components/paypal.js index b65a22c58..f0dc84f13 100644 --- a/modules/ppcp-blocks/resources/js/Components/paypal.js +++ b/modules/ppcp-blocks/resources/js/Components/paypal.js @@ -4,7 +4,6 @@ import { mergeWcAddress, paypalAddressToWc, paypalOrderToWcAddresses, - paypalSubscriptionToWcAddresses, } from '../Helper/Address'; import { convertKeysToSnakeCase } from '../Helper/Helper'; import buttonModuleWatcher from '../../../../ppcp-button/resources/js/modules/ButtonModuleWatcher'; @@ -13,6 +12,14 @@ import { cartHasSubscriptionProducts, isPayPalSubscription, } from '../Helper/Subscription'; +import { + createOrder, + createSubscription, + createVaultSetupToken, + handleApprove, + handleApproveSubscription, + onApproveSavePayment, +} from '../paypal-config'; const PAYPAL_GATEWAY_ID = 'ppcp-gateway'; @@ -114,156 +121,6 @@ export const PayPalComponent = ( { setContinuationFilled( true ); }, [ shippingData, continuationFilled ] ); - const createOrder = async ( data ) => { - try { - const requestBody = { - nonce: config.scriptData.ajax.create_order.nonce, - bn_code: '', - context: config.scriptData.context, - payment_method: 'ppcp-gateway', - funding_source: window.ppcpFundingSource ?? 'paypal', - createaccount: false, - ...( data?.paymentSource && { - payment_source: data.paymentSource, - } ), - }; - - const res = await fetch( - config.scriptData.ajax.create_order.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( requestBody ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( json.data?.details?.length > 0 ) { - throw new Error( - json.data.details - .map( ( d ) => `${ d.issue } ${ d.description }` ) - .join( '
' ) - ); - } else if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - - return json.data.id; - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - - const createSubscription = async ( data, actions ) => { - let planId = config.scriptData.subscription_plan_id; - if ( - config.scriptData - .variable_paypal_subscription_variation_from_cart !== '' - ) { - planId = - config.scriptData - .variable_paypal_subscription_variation_from_cart; - } - - return actions.subscription.create( { - plan_id: planId, - } ); - }; - - const handleApproveSubscription = async ( data, actions ) => { - try { - const subscription = await actions.subscription.get(); - - if ( subscription ) { - const addresses = - paypalSubscriptionToWcAddresses( subscription ); - - const promises = [ - // save address on server - wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - billing_address: addresses.billingAddress, - shipping_address: addresses.shippingAddress, - } ), - ]; - if ( shouldHandleShippingInPayPal() ) { - // set address in UI - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setBillingAddress( addresses.billingAddress ) - ); - if ( shippingData.needsShipping ) { - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setShippingAddress( addresses.shippingAddress ) - ); - } - } - await Promise.all( promises ); - } - - setPaypalOrder( subscription ); - - const res = await fetch( - config.scriptData.ajax.approve_subscription.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.scriptData.ajax.approve_subscription - .nonce, - order_id: data.orderID, - subscription_id: data.subscriptionID, - } ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( - typeof actions !== 'undefined' && - typeof actions.restart !== 'undefined' - ) { - return actions.restart(); - } - if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - - if ( ! shouldskipFinalConfirmation() ) { - location.href = getCheckoutRedirectUrl(); - } else { - setGotoContinuationOnError( true ); - enforcePaymentMethodForCart(); - onSubmit(); - } - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - const getCheckoutRedirectUrl = () => { const checkoutUrl = new URL( config.scriptData.redirect ); // sometimes some browsers may load some kind of cached version of the page, @@ -275,87 +132,6 @@ export const PayPalComponent = ( { return checkoutUrl.toString(); }; - const handleApprove = async ( data, actions ) => { - try { - const order = await actions.order.get(); - - if ( order ) { - const addresses = paypalOrderToWcAddresses( order ); - - const promises = [ - // save address on server - wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - billing_address: addresses.billingAddress, - shipping_address: addresses.shippingAddress, - } ), - ]; - if ( shouldHandleShippingInPayPal() ) { - // set address in UI - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setBillingAddress( addresses.billingAddress ) - ); - if ( shippingData.needsShipping ) { - promises.push( - wp.data - .dispatch( 'wc/store/cart' ) - .setShippingAddress( addresses.shippingAddress ) - ); - } - } - await Promise.all( promises ); - } - - setPaypalOrder( order ); - - const res = await fetch( - config.scriptData.ajax.approve_order.endpoint, - { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.scriptData.ajax.approve_order.nonce, - order_id: data.orderID, - funding_source: window.ppcpFundingSource ?? 'paypal', - } ), - } - ); - - const json = await res.json(); - - if ( ! json.success ) { - if ( - typeof actions !== 'undefined' && - typeof actions.restart !== 'undefined' - ) { - return actions.restart(); - } - if ( json.data?.message ) { - throw new Error( json.data.message ); - } - - throw new Error( config.scriptData.labels.error.generic ); - } - - if ( ! shouldskipFinalConfirmation() ) { - location.href = getCheckoutRedirectUrl(); - } else { - setGotoContinuationOnError( true ); - enforcePaymentMethodForCart(); - onSubmit(); - } - } catch ( err ) { - console.error( err ); - - onError( err.message ); - - onClose(); - - throw err; - } - }; - useEffect( () => { const unsubscribe = onCheckoutValidation( () => { if ( config.scriptData.continuation ) { @@ -600,11 +376,25 @@ export const PayPalComponent = ( { buttonModuleWatcher.registerContextBootstrap( config.scriptData.context, { - createOrder: () => { - return createOrder(); + createOrder: ( data ) => { + return createOrder( data, config, onError, onClose ); }, onApprove: ( data, actions ) => { - return handleApprove( data, actions ); + return handleApprove( + data, + actions, + config, + shouldHandleShippingInPayPal, + shippingData, + setPaypalOrder, + shouldskipFinalConfirmation, + getCheckoutRedirectUrl, + setGotoContinuationOnError, + enforcePaymentMethodForCart, + onSubmit, + onError, + onClose + ); }, } ); @@ -660,61 +450,6 @@ export const PayPalComponent = ( { }; }; - const createVaultSetupToken = async () => { - return fetch( config.scriptData.ajax.create_setup_token.endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify( { - nonce: config.scriptData.ajax.create_setup_token.nonce, - payment_method: 'ppcp-gateway', - } ), - } ) - .then( ( response ) => response.json() ) - .then( ( result ) => { - return result.data.id; - } ) - .catch( ( err ) => { - console.error( err ); - } ); - }; - - const onApproveSavePayment = async ( { vaultSetupToken } ) => { - let endpoint = - config.scriptData.ajax.create_payment_token_for_guest.endpoint; - let bodyContent = { - nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce, - vault_setup_token: vaultSetupToken, - }; - - if ( config.scriptData.user.is_logged_in ) { - endpoint = config.scriptData.ajax.create_payment_token.endpoint; - - bodyContent = { - nonce: config.scriptData.ajax.create_payment_token.nonce, - vault_setup_token: vaultSetupToken, - is_free_trial_cart: config.scriptData.is_free_trial_cart, - }; - } - - const response = await fetch( endpoint, { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify( bodyContent ), - } ); - - const result = await response.json(); - if ( result.success === true ) { - onSubmit(); - } - - console.error( result ); - }; - if ( cartHasSubscriptionProducts( config.scriptData ) && config.scriptData.is_free_trial_cart @@ -725,8 +460,10 @@ export const PayPalComponent = ( { onClick={ handleClick } onCancel={ onClose } onError={ onClose } - createVaultSetupToken={ createVaultSetupToken } - onApprove={ onApproveSavePayment } + createVaultSetupToken={ () => createVaultSetupToken( config ) } + onApprove={ ( { vaultSetupToken } ) => + onApproveSavePayment( vaultSetupToken, config, onSubmit ) + } /> ); } @@ -739,8 +476,26 @@ export const PayPalComponent = ( { onClick={ handleClick } onCancel={ onClose } onError={ onClose } - createSubscription={ createSubscription } - onApprove={ handleApproveSubscription } + createSubscription={ ( data, actions ) => + createSubscription( data, actions, config ) + } + onApprove={ ( data, actions ) => + handleApproveSubscription( + data, + actions, + config, + shouldHandleShippingInPayPal, + shippingData, + setPaypalOrder, + shouldskipFinalConfirmation, + getCheckoutRedirectUrl, + setGotoContinuationOnError, + enforcePaymentMethodForCart, + onSubmit, + onError, + onClose + ) + } onShippingOptionsChange={ getOnShippingOptionsChange( fundingSource ) } @@ -758,8 +513,26 @@ export const PayPalComponent = ( { onClick={ handleClick } onCancel={ onClose } onError={ onClose } - createOrder={ createOrder } - onApprove={ handleApprove } + createOrder={ ( data ) => + createOrder( data, config, onError, onClose ) + } + onApprove={ ( data, actions ) => + handleApprove( + data, + actions, + config, + shouldHandleShippingInPayPal, + shippingData, + setPaypalOrder, + shouldskipFinalConfirmation, + getCheckoutRedirectUrl, + setGotoContinuationOnError, + enforcePaymentMethodForCart, + onSubmit, + onError, + onClose + ) + } onShippingOptionsChange={ getOnShippingOptionsChange( fundingSource ) } diff --git a/modules/ppcp-blocks/resources/js/paypal-config.js b/modules/ppcp-blocks/resources/js/paypal-config.js new file mode 100644 index 000000000..d78ee14db --- /dev/null +++ b/modules/ppcp-blocks/resources/js/paypal-config.js @@ -0,0 +1,316 @@ +import { + paypalOrderToWcAddresses, + paypalSubscriptionToWcAddresses, +} from './Helper/Address'; + +export const createOrder = async ( data, config, onError, onClose ) => { + try { + const requestBody = { + nonce: config.scriptData.ajax.create_order.nonce, + bn_code: '', + context: config.scriptData.context, + payment_method: 'ppcp-gateway', + funding_source: window.ppcpFundingSource ?? 'paypal', + createaccount: false, + ...( data?.paymentSource && { + payment_source: data.paymentSource, + } ), + }; + + const res = await fetch( config.scriptData.ajax.create_order.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( requestBody ), + } ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( json.data?.details?.length > 0 ) { + throw new Error( + json.data.details + .map( ( d ) => `${ d.issue } ${ d.description }` ) + .join( '
' ) + ); + } else if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + + return json.data.id; + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } +}; + +export const handleApprove = async ( + data, + actions, + config, + shouldHandleShippingInPayPal, + shippingData, + setPaypalOrder, + shouldskipFinalConfirmation, + getCheckoutRedirectUrl, + setGotoContinuationOnError, + enforcePaymentMethodForCart, + onSubmit, + onError, + onClose +) => { + try { + const order = await actions.order.get(); + + if ( order ) { + const addresses = paypalOrderToWcAddresses( order ); + + const promises = [ + // save address on server + wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + billing_address: addresses.billingAddress, + shipping_address: addresses.shippingAddress, + } ), + ]; + if ( shouldHandleShippingInPayPal() ) { + // set address in UI + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setBillingAddress( addresses.billingAddress ) + ); + if ( shippingData.needsShipping ) { + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setShippingAddress( addresses.shippingAddress ) + ); + } + } + await Promise.all( promises ); + } + + setPaypalOrder( order ); + + const res = await fetch( + config.scriptData.ajax.approve_order.endpoint, + { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.scriptData.ajax.approve_order.nonce, + order_id: data.orderID, + funding_source: window.ppcpFundingSource ?? 'paypal', + } ), + } + ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( + typeof actions !== 'undefined' && + typeof actions.restart !== 'undefined' + ) { + return actions.restart(); + } + if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + + if ( ! shouldskipFinalConfirmation() ) { + location.href = getCheckoutRedirectUrl(); + } else { + setGotoContinuationOnError( true ); + enforcePaymentMethodForCart(); + onSubmit(); + } + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } +}; + +export const createSubscription = async ( data, actions, config ) => { + let planId = config.scriptData.subscription_plan_id; + if ( + config.scriptData.variable_paypal_subscription_variation_from_cart !== + '' + ) { + planId = + config.scriptData.variable_paypal_subscription_variation_from_cart; + } + + return actions.subscription.create( { + plan_id: planId, + } ); +}; + +export const handleApproveSubscription = async ( + data, + actions, + config, + shouldHandleShippingInPayPal, + shippingData, + setPaypalOrder, + shouldskipFinalConfirmation, + getCheckoutRedirectUrl, + setGotoContinuationOnError, + enforcePaymentMethodForCart, + onSubmit, + onError, + onClose +) => { + try { + const subscription = await actions.subscription.get(); + + if ( subscription ) { + const addresses = paypalSubscriptionToWcAddresses( subscription ); + + const promises = [ + // save address on server + wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { + billing_address: addresses.billingAddress, + shipping_address: addresses.shippingAddress, + } ), + ]; + if ( shouldHandleShippingInPayPal() ) { + // set address in UI + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setBillingAddress( addresses.billingAddress ) + ); + if ( shippingData.needsShipping ) { + promises.push( + wp.data + .dispatch( 'wc/store/cart' ) + .setShippingAddress( addresses.shippingAddress ) + ); + } + } + await Promise.all( promises ); + } + + setPaypalOrder( subscription ); + + const res = await fetch( + config.scriptData.ajax.approve_subscription.endpoint, + { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify( { + nonce: config.scriptData.ajax.approve_subscription.nonce, + order_id: data.orderID, + subscription_id: data.subscriptionID, + } ), + } + ); + + const json = await res.json(); + + if ( ! json.success ) { + if ( + typeof actions !== 'undefined' && + typeof actions.restart !== 'undefined' + ) { + return actions.restart(); + } + if ( json.data?.message ) { + throw new Error( json.data.message ); + } + + throw new Error( config.scriptData.labels.error.generic ); + } + + if ( ! shouldskipFinalConfirmation() ) { + location.href = getCheckoutRedirectUrl(); + } else { + setGotoContinuationOnError( true ); + enforcePaymentMethodForCart(); + onSubmit(); + } + } catch ( err ) { + console.error( err ); + + onError( err.message ); + + onClose(); + + throw err; + } +}; + +export const createVaultSetupToken = async ( config ) => { + return fetch( config.scriptData.ajax.create_setup_token.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( { + nonce: config.scriptData.ajax.create_setup_token.nonce, + payment_method: 'ppcp-gateway', + } ), + } ) + .then( ( response ) => response.json() ) + .then( ( result ) => { + return result.data.id; + } ) + .catch( ( err ) => { + console.error( err ); + } ); +}; + +export const onApproveSavePayment = async ( + vaultSetupToken, + config, + onSubmit +) => { + let endpoint = + config.scriptData.ajax.create_payment_token_for_guest.endpoint; + let bodyContent = { + nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce, + vault_setup_token: vaultSetupToken, + }; + + if ( config.scriptData.user.is_logged_in ) { + endpoint = config.scriptData.ajax.create_payment_token.endpoint; + + bodyContent = { + nonce: config.scriptData.ajax.create_payment_token.nonce, + vault_setup_token: vaultSetupToken, + is_free_trial_cart: config.scriptData.is_free_trial_cart, + }; + } + + const response = await fetch( endpoint, { + method: 'POST', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( bodyContent ), + } ); + + const result = await response.json(); + if ( result.success === true ) { + onSubmit(); + } + + console.error( result ); +}; From 21eb11aee892ec5af835afbc1d88e203cfcb6f6d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 4 Dec 2024 16:34:29 +0100 Subject: [PATCH 041/169] Remove not used methods --- .../resources/js/Components/paypal.js | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/paypal.js b/modules/ppcp-blocks/resources/js/Components/paypal.js index f0dc84f13..c9bb1ad88 100644 --- a/modules/ppcp-blocks/resources/js/Components/paypal.js +++ b/modules/ppcp-blocks/resources/js/Components/paypal.js @@ -177,8 +177,6 @@ export const PayPalComponent = ( { let handleShippingOptionsChange = null; let handleShippingAddressChange = null; - let handleSubscriptionShippingOptionsChange = null; - let handleSubscriptionShippingAddressChange = null; if ( shippingData.needsShipping && shouldHandleShippingInPayPal() ) { handleShippingOptionsChange = async ( data, actions ) => { @@ -244,55 +242,6 @@ export const PayPalComponent = ( { actions.reject(); } }; - - handleSubscriptionShippingOptionsChange = async ( data, actions ) => { - try { - const shippingOptionId = data.selectedShippingOption?.id; - if ( shippingOptionId ) { - await wp.data - .dispatch( 'wc/store/cart' ) - .selectShippingRate( shippingOptionId ); - await shippingData.setSelectedRates( shippingOptionId ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; - - handleSubscriptionShippingAddressChange = async ( data, actions ) => { - try { - const address = paypalAddressToWc( - convertKeysToSnakeCase( data.shippingAddress ) - ); - - await wp.data.dispatch( 'wc/store/cart' ).updateCustomerData( { - shipping_address: address, - } ); - - await shippingData.setShippingAddress( address ); - - const res = await fetch( config.ajax.update_shipping.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify( { - nonce: config.ajax.update_shipping.nonce, - order_id: data.orderID, - } ), - } ); - - const json = await res.json(); - - if ( ! json.success ) { - throw new Error( json.data.message ); - } - } catch ( e ) { - console.error( e ); - - actions.reject(); - } - }; } useEffect( () => { From 87ff0c23a004efc0fe194662b5680032ef2d43f6 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Wed, 4 Dec 2024 18:36:35 +0400 Subject: [PATCH 042/169] 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 0014274b91dd0eecac70d46cf41203cdf0dcb20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Thu, 5 Dec 2024 13:20:47 +0100 Subject: [PATCH 043/169] fix psalm error --- psalm.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psalm.xml.dist b/psalm.xml.dist index 46f4e8a49..6c6c87d89 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -35,13 +35,13 @@ - + From 632b7f930dc14f2046281eb56d380e29ce08ba08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Thu, 5 Dec 2024 14:18:09 +0100 Subject: [PATCH 044/169] update codesniffer --- composer.json | 3 ++- composer.lock | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index af87af977..1ba2c98f4 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "php-stubs/wordpress-stubs": "^v6.7.1", "php-stubs/woocommerce-stubs": "^v8.9.1", "vimeo/psalm": "^4.0", - "vlucas/phpdotenv": "^5" + "vlucas/phpdotenv": "^5", + "squizlabs/php_codesniffer": "^3.11" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 91471c2bc..a0d380480 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "89ec4e31d0bd8fc8770ffd065dff81e0", + "content-hash": "4fd2099dc7f045a1271b7db3d215fa24", "packages": [ { "name": "container-interop/service-provider", @@ -836,16 +836,16 @@ }, { "name": "brain/monkey", - "version": "2.6.1", + "version": "2.6.2", "source": { "type": "git", "url": "https://github.com/Brain-WP/BrainMonkey.git", - "reference": "a31c84515bb0d49be9310f52ef1733980ea8ffbb" + "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/a31c84515bb0d49be9310f52ef1733980ea8ffbb", - "reference": "a31c84515bb0d49be9310f52ef1733980ea8ffbb", + "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/d95a9d895352c30f47604ad1b825ab8fa9d1a373", + "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373", "shasum": "" }, "require": { @@ -861,8 +861,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-version/1": "1.x-dev", - "dev-master": "2.0.x-dev" + "dev-master": "2.x-dev", + "dev-version/1": "1.x-dev" } }, "autoload": { @@ -902,7 +902,7 @@ "issues": "https://github.com/Brain-WP/BrainMonkey/issues", "source": "https://github.com/Brain-WP/BrainMonkey" }, - "time": "2021-11-11T15:53:55+00:00" + "time": "2024-08-29T20:15:04+00:00" }, { "name": "composer/package-versions-deprecated", @@ -4285,16 +4285,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.2", + "version": "3.11.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", - "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", "shasum": "" }, "require": { @@ -4361,7 +4361,7 @@ "type": "open_collective" } ], - "time": "2024-07-21T23:26:44+00:00" + "time": "2024-11-16T12:02:36+00:00" }, { "name": "symfony/console", @@ -5214,10 +5214,10 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev", - "dev-3.x": "3.x-dev", + "dev-1.x": "1.x-dev", "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" + "dev-3.x": "3.x-dev", + "dev-master": "4.x-dev" } }, "autoload": { From 84060d1b2903cc4c692c544fc08d6ba032aa5d2e Mon Sep 17 00:00:00 2001 From: Himad M Date: Thu, 5 Dec 2024 09:33:24 -0400 Subject: [PATCH 045/169] 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 @@ class OnboardingProfile extends AbstractDataModel { 'completed' => false, 'step' => 0, 'is_casual_seller' => null, - 'are_optional_payment_methods_enabled' => true, + 'are_optional_payment_methods_enabled' => null, 'products' => array(), ); } From e2ec95d52fc5006f41ad7d3d13dc1a8c3f8ac148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Thu, 5 Dec 2024 14:41:53 +0100 Subject: [PATCH 046/169] test newer psalm version --- composer.json | 2 +- composer.lock | 445 +++++++++++++++++++++++++------------------------ psalm.xml.dist | 3 +- 3 files changed, 234 insertions(+), 216 deletions(-) diff --git a/composer.json b/composer.json index 1ba2c98f4..8f2a886fc 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "brain/monkey": "^2.4", "php-stubs/wordpress-stubs": "^v6.7.1", "php-stubs/woocommerce-stubs": "^v8.9.1", - "vimeo/psalm": "^4.0", + "vimeo/psalm": "^5.0", "vlucas/phpdotenv": "^5", "squizlabs/php_codesniffer": "^3.11" }, diff --git a/composer.lock b/composer.lock index a0d380480..24179b213 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4fd2099dc7f045a1271b7db3d215fa24", + "content-hash": "67d24d500b2d30db8bd3e24e740b8f43", "packages": [ { "name": "container-interop/service-provider", @@ -904,79 +904,6 @@ }, "time": "2024-08-29T20:15:04+00:00" }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, { "name": "composer/pcre", "version": "3.3.1", @@ -1648,6 +1575,67 @@ }, "time": "2022-03-02T22:36:06+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.3", @@ -2134,59 +2122,6 @@ }, "time": "2024-03-17T08:10:35+00:00" }, - { - "name": "openlss/lib-array2xml", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "LSS": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } - ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", - "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" - ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, - "time": "2019-03-29T20:06:56+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -4283,6 +4218,70 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "spatie/array-to-xml", + "version": "2.17.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^7.4|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "phpunit/phpunit": "^9.0", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-26T08:22:07+00:00" + }, { "name": "squizlabs/php_codesniffer", "version": "3.11.1", @@ -4529,6 +4528,73 @@ ], "time": "2023-01-24T14:02:46+00:00" }, + { + "name": "symfony/filesystem", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", + "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-22T13:05:35+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.30.0", @@ -5144,24 +5210,24 @@ }, { "name": "vimeo/psalm", - "version": "4.30.0", + "version": "5.26.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69" + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/d0bc6e25d89f649e4f36a534f330f8bb4643dd69", - "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", "shasum": "" }, "require": { "amphp/amp": "^2.4.2", "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.8.0", + "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", "ext-ctype": "*", "ext-dom": "*", @@ -5170,35 +5236,38 @@ "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.5", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", - "openlss/lib-array2xml": "^1.0", - "php": "^7.1|^8", - "sebastian/diff": "^3.0 || ^4.0", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", - "symfony/polyfill-php80": "^1.25", - "webmozart/path-util": "^2.3" + "nikic/php-parser": "^4.17", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "brianium/paratest": "^4.0||^6.0", + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpdocumentor/reflection-docblock": "^5", - "phpmyadmin/sql-parser": "5.1.0||dev-master", - "phpspec/prophecy": ">=1.9.0", - "phpstan/phpdoc-parser": "1.2.* || 1.6.4", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.16", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^4.3 || ^5.0 || ^6.0", - "weirdan/prophecy-shim": "^1.0 || ^2.0" + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "suggest": { "ext-curl": "In order to send data to shepherd", @@ -5211,20 +5280,17 @@ "psalm-refactor", "psalter" ], - "type": "library", + "type": "project", "extra": { "branch-alias": { "dev-1.x": "1.x-dev", "dev-2.x": "2.x-dev", "dev-3.x": "3.x-dev", - "dev-master": "4.x-dev" + "dev-4.x": "4.x-dev", + "dev-master": "5.x-dev" } }, "autoload": { - "files": [ - "src/functions.php", - "src/spl_object_id.php" - ], "psr-4": { "Psalm\\": "src/Psalm/" } @@ -5242,13 +5308,15 @@ "keywords": [ "code", "inspection", - "php" + "php", + "static analysis" ], "support": { + "docs": "https://psalm.dev/docs", "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.30.0" + "source": "https://github.com/vimeo/psalm" }, - "time": "2022-11-06T20:37:08+00:00" + "time": "2024-09-08T18:53:08+00:00" }, { "name": "vlucas/phpdotenv", @@ -5392,57 +5460,6 @@ }, "time": "2022-06-03T18:03:27+00:00" }, - { - "name": "webmozart/path-util", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/path-util.git", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "webmozart/assert": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\PathUtil\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", - "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" - }, - "abandoned": "symfony/filesystem", - "time": "2015-12-17T08:42:14+00:00" - }, { "name": "woocommerce/woocommerce-sniffs", "version": "0.1.3", diff --git a/psalm.xml.dist b/psalm.xml.dist index 6c6c87d89..90f234f0b 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -58,7 +58,6 @@ - @@ -164,6 +163,8 @@ + + From 2302f6ce18c06a45e36f9f5ee313392f4cbc4975 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 5 Dec 2024 14:52:10 +0100 Subject: [PATCH 047/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20translat?= =?UTF-8?q?ions=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 048/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Restructure=20inte?= =?UTF-8?q?rnal=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 ); + return { + handleError: ( res, genericMessage ) => { + console.error( 'Connection error', res ); + createErrorNotice( res?.message ?? genericMessage ); + }, + handleSuccess: async () => { + createSuccessNotice( MESSAGES.CONNECTED ); + return setCompleted( true ); + }, + createErrorNotice, }; - - const handleServerSuccess = () => { - createSuccessNotice( MESSAGES.CONNECTED ); - return setCompleted( true ); - }; - - 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 049/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Restructure=20Sand?= =?UTF-8?q?box=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 050/169] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20re-organization?= =?UTF-8?q?=20of=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 051/169] =?UTF-8?q?=E2=9C=A8=20Add=20new=20hook=20for=20pr?= =?UTF-8?q?oduction=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 052/169] =?UTF-8?q?=E2=9C=A8=20Add=20production=20login=20?= =?UTF-8?q?hook=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 053/169] =?UTF-8?q?=F0=9F=91=94=20Start=20to=20customize?= =?UTF-8?q?=20the=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 @@ use Psr\Log\LoggerInterface; /** * 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 054/169] 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 @@ return array( $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 array( 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 055/169] =?UTF-8?q?=E2=9C=A8=20Add=20automatic=20popup=20c?= =?UTF-8?q?ompletion=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 056/169] =?UTF-8?q?=E2=9C=A8=20Add=20a=20default=20?= =?UTF-8?q?=E2=80=9Creset=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 057/169] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Cor?= =?UTF-8?q?rect=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 058/169] =?UTF-8?q?=E2=9C=A8=20Replace=20isBusy=20with=20n?= =?UTF-8?q?ew=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 059/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20some=20?= =?UTF-8?q?action=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 060/169] =?UTF-8?q?=E2=9C=A8=20Use=20new=20actitiy-state?= =?UTF-8?q?=20for=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 061/169] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Minor=20performanc?= =?UTF-8?q?e=20tweaks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/AdvancedOptionsForm.js | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 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 ); } - }; + }, [ clientId, clientSecret, clientValid, secretValid ] ); - const handleManualConnect = () => - handleConnectViaIdAndSecret( { - validation: validateManualConnectionForm, - } ); + 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 062/169] =?UTF-8?q?=E2=9C=A8=20Disable=20the=20Connect-but?= =?UTF-8?q?ton=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 063/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rearrange=20code,?= =?UTF-8?q?=20minor=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 064/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20callback?= =?UTF-8?q?=20methods,=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 065/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20REST=20cont?= =?UTF-8?q?roller=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 @@ class CommonRestEndpoint extends RestEndpoint { $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 @@ class CommonRestEndpoint extends RestEndpoint { 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 066/169] =?UTF-8?q?=E2=9C=A8=20Add=20merchant-connection?= =?UTF-8?q?=20data=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 @@ class CommonSettings extends AbstractDataModel { '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 @@ class CommonSettings extends AbstractDataModel { 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 067/169] =?UTF-8?q?=E2=9C=A8=20Add=20merchant=20details=20?= =?UTF-8?q?to=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 @@ class CommonRestEndpoint extends RestEndpoint { ); $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 @@ class CommonRestEndpoint extends RestEndpoint { 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 068/169] =?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 @@ class CommonRestEndpoint extends RestEndpoint { ), ) ); + + 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 @@ class CommonRestEndpoint extends RestEndpoint { 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 069/169] =?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 070/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Minor:=20Freeze=20?= =?UTF-8?q?read-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 071/169] =?UTF-8?q?=E2=9C=A8=20Integrate=20merchant=20chec?= =?UTF-8?q?ks=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 072/169] 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 | 127 ++-- .../_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 | 267 ++------- .../Screens/Overview/TabPaymentMethods.js | 36 +- .../Screens/Overview/TabSettings.js | 2 + .../TabSettingsElements/Blocks/OrderIntent.js | 103 ++-- .../Blocks/OtherSettings.js | 83 ++- .../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, 1887 insertions(+), 1541 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; + } - &:hover { - transform: rotate(45deg); - cursor: pointer; + &__description { + p { + margin: 0; + color: $color-text-tertiary; + @include font(13, 20, 400); + } + + margin: 0 0 12px 0; + } + + &__title { + @include font(13, 20, 500); + color: $color-black; + display: block; + } + + &__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; } } - - 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 - ) - } - > - { 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 ( - - ); - } ) } -
+
+ } + contentItems={ featuresDefault.map( ( feature ) => ( + + ) ) } + />
); }; -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,49 +1,8 @@ -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'; - -const OtherSettings = ( { settings, updateFormValue } ) => { - return ( - - - - ); -}; +import { + AccordionSettingsBlock, + SelectSettingsBlock, +} from '../../../../ReusableComponents/SettingsBlocks'; const creditCardExamples = [ { value: '', label: __( 'Select', 'woocommerce-paypal-payments' ) }, @@ -63,4 +22,38 @@ const creditCardExamples = [ }, ]; +const OtherSettings = ( { settings, updateFormValue } ) => { + return ( + + + + ); +}; + 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 90f81cf2edef633e9c0e3192bc935cfd1ecb1bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Mon, 9 Dec 2024 11:45:38 +0100 Subject: [PATCH 073/169] revert changes and try to run psalm only php 7.4 --- .github/workflows/php.yml | 2 + composer.json | 9 +- composer.lock | 513 ++++++++++++++++++-------------------- psalm.xml.dist | 5 +- 4 files changed, 257 insertions(+), 272 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 5dfe9be79..102b6d6a4 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -31,6 +31,8 @@ jobs: - name: Psalm run: ./vendor/bin/psalm --show-info=false --threads=8 --diff + with: + php-version: '7.4' - name: Run PHPCS run: ./vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1 src modules woocommerce-paypal-payments.php --extensions=php diff --git a/composer.json b/composer.json index 8f2a886fc..17ecd3eda 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,10 @@ "woocommerce/woocommerce-sniffs": "^0.1.0", "phpunit/phpunit": "^7.0 | ^8.0 | ^9.0", "brain/monkey": "^2.4", - "php-stubs/wordpress-stubs": "^v6.7.1", - "php-stubs/woocommerce-stubs": "^v8.9.1", - "vimeo/psalm": "^5.0", - "vlucas/phpdotenv": "^5", - "squizlabs/php_codesniffer": "^3.11" + "php-stubs/wordpress-stubs": "^5.0@stable", + "php-stubs/woocommerce-stubs": "^8.0@stable", + "vimeo/psalm": "^4.0", + "vlucas/phpdotenv": "^5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 24179b213..180c71364 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "67d24d500b2d30db8bd3e24e740b8f43", + "content-hash": "2fa610ed883c0868838d3008b7127cbf", "packages": [ { "name": "container-interop/service-provider", @@ -836,16 +836,16 @@ }, { "name": "brain/monkey", - "version": "2.6.2", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/Brain-WP/BrainMonkey.git", - "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373" + "reference": "a31c84515bb0d49be9310f52ef1733980ea8ffbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/d95a9d895352c30f47604ad1b825ab8fa9d1a373", - "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373", + "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/a31c84515bb0d49be9310f52ef1733980ea8ffbb", + "reference": "a31c84515bb0d49be9310f52ef1733980ea8ffbb", "shasum": "" }, "require": { @@ -861,8 +861,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev", - "dev-version/1": "1.x-dev" + "dev-version/1": "1.x-dev", + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -902,7 +902,80 @@ "issues": "https://github.com/Brain-WP/BrainMonkey/issues", "source": "https://github.com/Brain-WP/BrainMonkey" }, - "time": "2024-08-29T20:15:04+00:00" + "time": "2021-11-11T15:53:55+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" }, { "name": "composer/pcre", @@ -1575,67 +1648,6 @@ }, "time": "2022-03-02T22:36:06+00:00" }, - { - "name": "fidry/cpu-core-counter", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Tiny utility to get the number of CPU cores.", - "keywords": [ - "CPU", - "core" - ], - "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2024-08-06T10:04:20+00:00" - }, { "name": "graham-campbell/result-type", "version": "v1.1.3", @@ -2122,6 +2134,59 @@ }, "time": "2024-03-17T08:10:35+00:00" }, + { + "name": "openlss/lib-array2xml", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/nullivex/lib-array2xml.git", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "LSS": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bryan Tong", + "email": "bryan@nullivex.com", + "homepage": "https://www.nullivex.com" + }, + { + "name": "Tony Butler", + "email": "spudz76@gmail.com", + "homepage": "https://www.nullivex.com" + } + ], + "description": "Array2XML conversion library credit to lalit.org", + "homepage": "https://www.nullivex.com", + "keywords": [ + "array", + "array conversion", + "xml", + "xml conversion" + ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, + "time": "2019-03-29T20:06:56+00:00" + }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -2286,28 +2351,27 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.7.1", + "version": "v5.9.9", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1" + "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/83448e918bf06d1ed3d67ceb6a985fc266a02fd1", - "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/06c51c4863659ea9e9f4c2a23293728a677cb059", + "reference": "06c51c4863659ea9e9f4c2a23293728a677cb059", "shasum": "" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^4.13", - "php": "^7.4 || ^8.0", + "php": "^7.4 || ~8.0.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.4.1", - "phpstan/phpstan": "^1.11", + "phpdocumentor/reflection-docblock": "5.3", + "phpstan/phpstan": "^1.10.49", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", - "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -2328,9 +2392,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.7.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.9" }, - "time": "2024-11-24T03:57:09+00:00" + "time": "2024-04-14T17:16:00+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -3154,16 +3218,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.21", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { @@ -3178,7 +3242,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-code-coverage": "^9.2.31", "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.4", @@ -3237,7 +3301,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -3253,7 +3317,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T10:50:18+00:00" + "time": "2024-07-10T11:45:39+00:00" }, { "name": "sebastian/cli-parser", @@ -4218,82 +4282,18 @@ ], "time": "2020-09-28T06:39:44+00:00" }, - { - "name": "spatie/array-to-xml", - "version": "2.17.1", - "source": { - "type": "git", - "url": "https://github.com/spatie/array-to-xml.git", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": "^7.4|^8.0" - }, - "require-dev": { - "mockery/mockery": "^1.2", - "pestphp/pest": "^1.21", - "phpunit/phpunit": "^9.0", - "spatie/pest-plugin-snapshots": "^1.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\ArrayToXml\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://freek.dev", - "role": "Developer" - } - ], - "description": "Convert an array to xml", - "homepage": "https://github.com/spatie/array-to-xml", - "keywords": [ - "array", - "convert", - "xml" - ], - "support": { - "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2022-12-26T08:22:07+00:00" - }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.1", + "version": "3.10.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", "shasum": "" }, "require": { @@ -4360,7 +4360,7 @@ "type": "open_collective" } ], - "time": "2024-11-16T12:02:36+00:00" + "time": "2024-07-21T23:26:44+00:00" }, { "name": "symfony/console", @@ -4528,73 +4528,6 @@ ], "time": "2023-01-24T14:02:46+00:00" }, - { - "name": "symfony/filesystem", - "version": "v5.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/process": "^5.4|^6.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.45" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-10-22T13:05:35+00:00" - }, { "name": "symfony/polyfill-ctype", "version": "v1.30.0", @@ -5210,24 +5143,24 @@ }, { "name": "vimeo/psalm", - "version": "5.26.1", + "version": "4.30.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" + "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d0bc6e25d89f649e4f36a534f330f8bb4643dd69", + "reference": "d0bc6e25d89f649e4f36a534f330f8bb4643dd69", "shasum": "" }, "require": { "amphp/amp": "^2.4.2", "amphp/byte-stream": "^1.5", - "composer-runtime-api": "^2", + "composer/package-versions-deprecated": "^1.8.0", "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^2.0 || ^3.0", + "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", "ext-ctype": "*", "ext-dom": "*", @@ -5236,38 +5169,35 @@ "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.1", - "felixfbecker/language-server-protocol": "^1.5.2", - "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "felixfbecker/advanced-json-rpc": "^3.0.3", + "felixfbecker/language-server-protocol": "^1.5", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.17", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0", - "spatie/array-to-xml": "^2.17.0 || ^3.0", - "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" - }, - "conflict": { - "nikic/php-parser": "4.17.0" + "nikic/php-parser": "^4.13", + "openlss/lib-array2xml": "^1.0", + "php": "^7.1|^8", + "sebastian/diff": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.25", + "webmozart/path-util": "^2.3" }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { - "amphp/phpunit-util": "^2.0", - "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.9", + "bamarni/composer-bin-plugin": "^1.2", + "brianium/paratest": "^4.0||^6.0", "ext-curl": "*", - "mockery/mockery": "^1.5", - "nunomaduro/mock-final-classes": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpdoc-parser": "^1.6", - "phpunit/phpunit": "^9.6", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.6", - "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + "phpdocumentor/reflection-docblock": "^5", + "phpmyadmin/sql-parser": "5.1.0||dev-master", + "phpspec/prophecy": ">=1.9.0", + "phpstan/phpdoc-parser": "1.2.* || 1.6.4", + "phpunit/phpunit": "^9.0", + "psalm/plugin-phpunit": "^0.16", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.3 || ^5.0 || ^6.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" }, "suggest": { "ext-curl": "In order to send data to shepherd", @@ -5280,17 +5210,20 @@ "psalm-refactor", "psalter" ], - "type": "project", + "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.x-dev", + "dev-master": "4.x-dev", "dev-3.x": "3.x-dev", - "dev-4.x": "4.x-dev", - "dev-master": "5.x-dev" + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" } }, "autoload": { + "files": [ + "src/functions.php", + "src/spl_object_id.php" + ], "psr-4": { "Psalm\\": "src/Psalm/" } @@ -5308,15 +5241,13 @@ "keywords": [ "code", "inspection", - "php", - "static analysis" + "php" ], "support": { - "docs": "https://psalm.dev/docs", "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm" + "source": "https://github.com/vimeo/psalm/tree/4.30.0" }, - "time": "2024-09-08T18:53:08+00:00" + "time": "2022-11-06T20:37:08+00:00" }, { "name": "vlucas/phpdotenv", @@ -5460,6 +5391,57 @@ }, "time": "2022-06-03T18:03:27+00:00" }, + { + "name": "webmozart/path-util", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/path-util.git", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "webmozart/assert": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\PathUtil\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, + "abandoned": "symfony/filesystem", + "time": "2015-12-17T08:42:14+00:00" + }, { "name": "woocommerce/woocommerce-sniffs", "version": "0.1.3", @@ -5558,7 +5540,10 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "php-stubs/woocommerce-stubs": 0, + "php-stubs/wordpress-stubs": 0 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/psalm.xml.dist b/psalm.xml.dist index 90f234f0b..46f4e8a49 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -35,13 +35,13 @@ + - @@ -58,6 +58,7 @@ + @@ -163,8 +164,6 @@ - - From 157fe84befc97d10d9b7c71d862edc43d274f216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Mon, 9 Dec 2024 11:56:20 +0100 Subject: [PATCH 074/169] improve statement for action php 7.4 --- .github/workflows/php.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 102b6d6a4..00ff24fcb 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -30,9 +30,8 @@ jobs: run: vendor/bin/phpunit - name: Psalm + if: ${{ matrix.php-versions == '7.4' }} run: ./vendor/bin/psalm --show-info=false --threads=8 --diff - with: - php-version: '7.4' - name: Run PHPCS run: ./vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1 src modules woocommerce-paypal-payments.php --extensions=php From 71461633014cf82e4d07d7ef9eed380afc50cf7a Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 12:04:02 +0100 Subject: [PATCH 075/169] =?UTF-8?q?=E2=9C=A8=20Re-implement=20conditional?= =?UTF-8?q?=20OXXO=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 ef8af8af1dee8897d8f0c3b9194b0c443f46199f Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 9 Dec 2024 16:32:29 +0400 Subject: [PATCH 076/169] Remove the "debit and credit" card from data --- .../resources/js/data/settings/tab-styling-data.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js b/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js index 92bb32015..5a0853f00 100644 --- a/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js +++ b/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js @@ -57,10 +57,6 @@ export const paymentMethodOptions = [ value: 'paylater', label: __( 'Pay Later', 'woocommerce-paypal-payments' ), }, - { - value: 'card', - label: __( 'Debit or Credit Card', 'woocommerce-paypal-payments' ), - }, { value: 'googlepay', label: __( 'Google Pay', 'woocommerce-paypal-payments' ), From 1b557f1619e98d801be65b3033c71607be6b414e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 14:03:07 +0100 Subject: [PATCH 077/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20generic=20A?= =?UTF-8?q?ccordion=20more=20generic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_accordion-section.scss | 25 ++--- .../ReusableComponents/AccordionSection.js | 101 ++++++++++-------- .../resources/js/hooks/useAccordionState.js | 39 +++++++ 3 files changed, 106 insertions(+), 59 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, - initiallyOpen = null, - className = '', id = '', - children, + initiallyOpen = null, + description = '', + children = null, + className = '', + + // Layout components can be overridden by the caller + Header = DefaultHeader, + TitleWrapper = DefaultTitleWrapper, + Title = DefaultTitle, + Action = DefaultAction, + Description = DefaultDescription, } ) => { - const determineInitialState = () => { - if ( id && initiallyOpen === null ) { - return window.location.hash === `#${ id }`; - } - return !! initiallyOpen; - }; + const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } ); + const wrapperClasses = classNames( 'ppcp-r-accordion', className, { + 'ppcp--is-open': isOpen, + } ); - 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; - }; - - 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 86191be2f465d5002fc366ed126b6b3cfede9b20 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 9 Dec 2024 17:03:47 +0400 Subject: [PATCH 078/169] Disable the funding from preview for "Credit or debit cards" --- .../resources/js/Components/Screens/Overview/TabStyling.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js index df2983aea..ed967deb2 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js @@ -321,6 +321,7 @@ const SectionButtonPreview = ( { locationSettings } ) => { clientId: 'test', merchantId: 'QTQX5NP6N9WZU', components: 'buttons,googlepay', + 'disable-funding': 'card', 'buyer-country': 'US', currency: 'USD', } } From 2a9766fc76e4d2221a3a99f3b130707e5b941fc6 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 9 Dec 2024 14:43:27 +0100 Subject: [PATCH 079/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reuse=20the=20gene?= =?UTF-8?q?ric=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 080/169] 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 @@ class OnboardingProfile extends AbstractDataModel { $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 081/169] 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 082/169] =?UTF-8?q?=E2=9C=A8=20Introduce=20new=20BusyState?= =?UTF-8?q?Wrapper=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 ( <> - - + - + 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' + ) } + 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 083/169] =?UTF-8?q?=F0=9F=92=84=20Fix=20multiple=20spinner?= =?UTF-8?q?s=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 084/169] =?UTF-8?q?=E2=9C=A8=20Disable=20the=20browser=20w?= =?UTF-8?q?arning=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 085/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Only=20disable=20c?= =?UTF-8?q?onfirmation=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 086/169] =?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, + } ); - if ( ! onboardingProgress.completed ) { - return ; - } + const Content = useMemo( () => { + if ( ! onboardingProgress.isReady ) { + return ( + + ); + } - return ; + if ( ! onboardingProgress.completed ) { + return ; + } + + return ; + }, [ onboardingProgress ] ); + + return
{ Content }
; }; export default Settings; From c65ca2a00b0f2fa2650e8026225f788d74b5517a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Tue, 10 Dec 2024 12:31:55 +0100 Subject: [PATCH 087/169] removed disabled from button when sandbox is changed --- modules/ppcp-onboarding/resources/js/onboarding.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-onboarding/resources/js/onboarding.js b/modules/ppcp-onboarding/resources/js/onboarding.js index 5a6ab333a..06197afd6 100644 --- a/modules/ppcp-onboarding/resources/js/onboarding.js +++ b/modules/ppcp-onboarding/resources/js/onboarding.js @@ -345,6 +345,10 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) { const sandboxSwitchElement = document.querySelector( '#ppcp-sandbox_on' ); + sandboxSwitchElement?.addEventListener( 'click', () => { + document.querySelector( '.woocommerce-save-button' )?.removeAttribute( 'disabled' ); + }); + const validate = () => { const selectors = sandboxSwitchElement.checked ? sandboxCredentialElementsSelectors From 14f91218f6c720d0132bd44ad377fe2c91f7917a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Tue, 10 Dec 2024 13:23:20 +0100 Subject: [PATCH 088/169] when variable product is in stock there is no need to query all variations --- modules/ppcp-button/src/Assets/SmartButton.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index a08c20b27..fd1b93404 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1910,7 +1910,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages $in_stock = $product->is_in_stock(); - if ( $product->is_type( 'variable' ) ) { + if ( ! $in_stock && $product->is_type( 'variable' ) ) { /** * The method is defined in WC_Product_Variable class. * From 3d49241c5ec81aeb2e042b96d8aaccefeb62f982 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 10 Dec 2024 13:58:00 +0100 Subject: [PATCH 089/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Major=20button-sty?= =?UTF-8?q?ling=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); - border-radius: 50px; - padding: 15px 32px; - height: auto; + --button-disabled-color: var(--color-gray-500); + --button-disabled-background: transparent; + --button-disabled-border-color: transparent; + + /* style the button template */ + + &: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 149c5f5b38ebc7a8c1491f75cc212ea9e78872e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 09:01:44 +0100 Subject: [PATCH 094/169] add cvv and avs error messages to order note and removed old address match fields --- .../src/Entity/FraudProcessorResponse.php | 102 +++++++++++++++--- .../CreditCardOrderInfoHandlingTrait.php | 60 ++++------- 2 files changed, 109 insertions(+), 53 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index 2cc7c5480..de254f088 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -17,32 +17,32 @@ class FraudProcessorResponse { /** * The AVS response code. * - * @var string|null + * @var string */ - protected $avs_code; + protected string $avs_code; /** * The CVV response code. * - * @var string|null + * @var string */ - protected $cvv_code; + protected string $cvv2_code; /** * FraudProcessorResponse constructor. * * @param string|null $avs_code The AVS response code. - * @param string|null $cvv_code The CVV response code. + * @param string|null $cvv2_code The CVV response code. */ - public function __construct( ?string $avs_code, ?string $cvv_code ) { - $this->avs_code = $avs_code; - $this->cvv_code = $cvv_code; + public function __construct( ?string $avs_code, ?string $cvv2_code ) { + $this->avs_code = (string) $avs_code; + $this->cvv2_code = (string) $cvv2_code; } /** * Returns the AVS response code. * - * @return string|null + * @return string */ public function avs_code(): ?string { return $this->avs_code; @@ -51,10 +51,10 @@ class FraudProcessorResponse { /** * Returns the CVV response code. * - * @return string|null + * @return string */ public function cvv_code(): ?string { - return $this->cvv_code; + return $this->cvv2_code; } /** @@ -64,11 +64,83 @@ class FraudProcessorResponse { */ public function to_array(): array { return array( - 'avs_code' => $this->avs_code() ?: '', - 'address_match' => $this->avs_code() === 'M' ? 'Y' : 'N', - 'postal_match' => $this->avs_code() === 'M' ? 'Y' : 'N', - 'cvv_match' => $this->cvv_code() === 'M' ? 'Y' : 'N', + 'avs_code' => $this->avs_code(), + 'cvv2_code' => $this->cvv_code(), ); } + /** + * Retrieves the AVS (Address Verification System) code messages based on the AVS response code. + * + * Provides human-readable descriptions for various AVS response codes + * and returns the corresponding message for the given code. + * + * @return string The AVS response code message. If the code is not found, an error message is returned. + */ + public function get_avs_code_messages(): string { + if ( $this->avs_code() ) { + return ''; + } + $messages = array( + /* Visa, Mastercard, Discover, American Express */ + 'A' => __( 'A: Address - Address only (no ZIP code)', 'woocommerce-paypal-payments' ), + 'B' => __( 'B: International "A" - Address only (no ZIP code)', 'woocommerce-paypal-payments' ), + 'C' => __( 'C: International "N" - None. The transaction is declined.', 'woocommerce-paypal-payments' ), + 'D' => __( 'D: International "X" - Address and Postal Code', 'woocommerce-paypal-payments' ), + 'E' => __( 'E: Not allowed for MOTO (Internet/Phone) transactions - Not applicable. The transaction is declined.', 'woocommerce-paypal-payments' ), + 'F' => __( 'F: UK-specific "X" - Address and Postal Code', 'woocommerce-paypal-payments' ), + 'G' => __( 'G: Global Unavailable - Not applicable', 'woocommerce-paypal-payments' ), + 'I' => __( 'I: International Unavailable - Not applicable', 'woocommerce-paypal-payments' ), + 'M' => __( 'M: Address - Address and Postal Code', 'woocommerce-paypal-payments' ), + 'N' => __( 'N: No - None. The transaction is declined.', 'woocommerce-paypal-payments' ), + 'P' => __( 'P: Postal (International "Z") - Postal Code only (no Address)', 'woocommerce-paypal-payments' ), + 'R' => __( 'R: Retry - Not applicable', 'woocommerce-paypal-payments' ), + 'S' => __( 'S: Service not Supported - Not applicable', 'woocommerce-paypal-payments' ), + 'U' => __( 'U: Unavailable / Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), + 'W' => __( 'W: Whole ZIP - Nine-digit ZIP code (no Address)', 'woocommerce-paypal-payments' ), + 'X' => __( 'X: Exact match - Address and nine-digit ZIP code)', 'woocommerce-paypal-payments' ), + 'Y' => __( 'Y: Yes - Address and five-digit ZIP', 'woocommerce-paypal-payments' ), + 'Z' => __( 'Z: ZIP - Five-digit ZIP code (no Address)', 'woocommerce-paypal-payments' ), + /* Maestro */ + '0' => __( '0: All the address information matched.', 'woocommerce-paypal-payments' ), + '1' => __( '1: None of the address information matched. The transaction is declined.', 'woocommerce-paypal-payments' ), + '2' => __( '2: Part of the address information matched.', 'woocommerce-paypal-payments' ), + '3' => __( '3: The merchant did not provide AVS information. Not processed.', 'woocommerce-paypal-payments' ), + '4' => __( '4: Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), + ); + /* translators: %s is fraud AVS code */ + return $messages[ $this->avs_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->avs_code() ); + } + + /** + * Retrieves the CVV2 code message based on the CVV code provided. + * + * This method maps CVV response codes to their corresponding descriptive messages. + * + * @return string The descriptive message corresponding to the CVV2 code, or a formatted error message if the code is unrecognized. + */ + public function get_cvv2_code_messages(): string { + if ( $this->cvv_code() ) { + return ''; + } + $messages = array( + /* Visa, Mastercard, Discover, American Express */ + 'E' => __( 'E: Error - Unrecognized or Unknown response', 'woocommerce-paypal-payments' ), + 'I' => __( 'I: Invalid or Null', 'woocommerce-paypal-payments' ), + 'M' => __( 'M: Match or CSC', 'woocommerce-paypal-payments' ), + 'N' => __( 'N: No match', 'woocommerce-paypal-payments' ), + 'P' => __( 'P: Not processed', 'woocommerce-paypal-payments' ), + 'S' => __( 'S: Service not supported', 'woocommerce-paypal-payments' ), + 'U' => __( 'U: Unknown - Issuer is not certified', 'woocommerce-paypal-payments' ), + 'X' => __( 'X: No response / Service not available', 'woocommerce-paypal-payments' ), + /* Maestro */ + '0' => __( '0: Matched CVV2', 'woocommerce-paypal-payments' ), + '1' => __( '1: No match', 'woocommerce-paypal-payments' ), + '2' => __( '2: The merchant has not implemented CVV2 code handling', 'woocommerce-paypal-payments' ), + '3' => __( '3: Merchant has indicated that CVV2 is not present on card', 'woocommerce-paypal-payments' ), + '4' => __( '4: Service not available', 'woocommerce-paypal-payments' ), + ); + /* translators: %s is fraud CVV2 code */ + return $messages[ $this->cvv_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->cvv_code() ); + } } diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 51a3a741c..c909e5981 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -96,52 +96,36 @@ trait CreditCardOrderInfoHandlingTrait { return; } - $fraud_responses = $fraud->to_array(); $card_brand = $payment_source->properties()->brand ?? __( 'N/A', 'woocommerce-paypal-payments' ); $card_last_digits = $payment_source->properties()->last_digits ?? __( 'N/A', 'woocommerce-paypal-payments' ); - $avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' ); + $response_order_note_title = __( 'Card decline errors', 'woocommerce-paypal-payments' ); /* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */ - $avs_response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); - $avs_response_order_note_result_format = '
    -
  • %1$s
  • -
      -
    • %2$s
    • -
    • %3$s
    • -
    -
  • %4$s
  • -
  • %5$s
  • -
'; - $avs_response_order_note_result = sprintf( - $avs_response_order_note_result_format, - /* translators: %s is fraud AVS code */ - sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['avs_code'] ) ), - /* translators: %s is fraud AVS address match */ - sprintf( __( 'Address Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['address_match'] ) ), - /* translators: %s is fraud AVS postal match */ - sprintf( __( 'Postal Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['postal_match'] ) ), - /* translators: %s is card brand */ - sprintf( __( 'Card Brand: %s', 'woocommerce-paypal-payments' ), esc_html( $card_brand ) ), - /* translators: %s card last digits */ - sprintf( __( 'Card Last Digits: %s', 'woocommerce-paypal-payments' ), esc_html( $card_last_digits ) ) + $response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); + $response_order_note_result_format = '
    +
  • %1$s
  • +
  • %2$s
  • +
  • %3$s
  • +
  • %3$s
  • +
'; + $response_order_note_result = sprintf( + $response_order_note_result_format, + /* translators: %1$s is card brand and %2$s card last 4 digits */ + sprintf( __( 'Card: %1$s (%2$s)', 'woocommerce-paypal-payments' ), $card_brand, $card_last_digits ), + /* translators: %s is fraud AVS message */ + sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), $fraud->get_avs_code_messages() ), + /* translators: %s is fraud CVV message */ + sprintf( __( 'CVV: %s', 'woocommerce-paypal-payments' ), $fraud->get_cvv2_code_messages() ), ); - $avs_response_order_note = sprintf( - $avs_response_order_note_format, - esc_html( $avs_response_order_note_title ), - wp_kses_post( $avs_response_order_note_result ) + $response_order_note = sprintf( + $response_order_note_format, + esc_html( $response_order_note_title ), + wp_kses_post( $response_order_note_result ) ); - $wc_order->add_order_note( $avs_response_order_note ); - - $cvv_response_order_note_format = '
  • %1$s
'; - $cvv_response_order_note = sprintf( - $cvv_response_order_note_format, - /* translators: %s is fraud CVV match */ - sprintf( __( 'CVV2 Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['cvv_match'] ) ) - ); - $wc_order->add_order_note( $cvv_response_order_note ); + $wc_order->add_order_note( $response_order_note ); $meta_details = array_merge( - $fraud_responses, + $fraud->to_array(), array( 'card_brand' => $card_brand, 'card_last_digits' => $card_last_digits, From ebfd4b18878d4fa9b27c3ab408c89cb38783e88d Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 11 Dec 2024 09:23:25 +0100 Subject: [PATCH 095/169] 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 b2aff305808025f7cd92d5af119ff6cd3f5a8547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 09:27:59 +0100 Subject: [PATCH 096/169] fix psalm error --- .../src/Entity/FraudProcessorResponse.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index de254f088..fb0db00ba 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -108,7 +108,12 @@ class FraudProcessorResponse { '3' => __( '3: The merchant did not provide AVS information. Not processed.', 'woocommerce-paypal-payments' ), '4' => __( '4: Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), ); - /* translators: %s is fraud AVS code */ + /** + * Translators: %s is fraud AVS code + * + * @psalm-suppress PossiblyNullArrayOffset + * @psalm-suppress PossiblyNullArgument + */ return $messages[ $this->avs_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->avs_code() ); } @@ -140,7 +145,12 @@ class FraudProcessorResponse { '3' => __( '3: Merchant has indicated that CVV2 is not present on card', 'woocommerce-paypal-payments' ), '4' => __( '4: Service not available', 'woocommerce-paypal-payments' ), ); - /* translators: %s is fraud CVV2 code */ + /** + * Translators: %s is fraud CVV2 code + * + * @psalm-suppress PossiblyNullArrayOffset + * @psalm-suppress PossiblyNullArgument + */ return $messages[ $this->cvv_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->cvv_code() ); } } From 1b1546768b1dbebc7b493859e103a638fe5a628e Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 11 Dec 2024 09:31:52 +0100 Subject: [PATCH 097/169] 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 3a63a8e7d76ac885d382f2c6875700526d8eaa3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 09:57:48 +0100 Subject: [PATCH 098/169] fix phpcs error --- .../ppcp-api-client/src/Entity/FraudProcessorResponse.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index fb0db00ba..7a9d89666 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -108,12 +108,12 @@ class FraudProcessorResponse { '3' => __( '3: The merchant did not provide AVS information. Not processed.', 'woocommerce-paypal-payments' ), '4' => __( '4: Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), ); + /** - * Translators: %s is fraud AVS code - * * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ + /* translators: %s is fraud AVS code */ return $messages[ $this->avs_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->avs_code() ); } @@ -145,12 +145,12 @@ class FraudProcessorResponse { '3' => __( '3: Merchant has indicated that CVV2 is not present on card', 'woocommerce-paypal-payments' ), '4' => __( '4: Service not available', 'woocommerce-paypal-payments' ), ); + /** - * Translators: %s is fraud CVV2 code - * * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ + /* translators: %s is fraud CVV2 code */ return $messages[ $this->cvv_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->cvv_code() ); } } From c92e2455e12f2f927b9ec83887ea34ea8322597d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 10:24:03 +0100 Subject: [PATCH 099/169] fix phpcs error --- modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index 7a9d89666..a1c281e93 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -110,6 +110,8 @@ class FraudProcessorResponse { ); /** + * Psalm suppress + * * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ @@ -147,6 +149,8 @@ class FraudProcessorResponse { ); /** + * Psalm suppress + * * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ From 486c0e683987a2d1fd4e88e1f5cf120702e93a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 14:16:09 +0100 Subject: [PATCH 100/169] removed translation, improved wordings and fixes --- .../src/Entity/FraudProcessorResponse.php | 94 ++++++++++--------- .../CreditCardOrderInfoHandlingTrait.php | 7 +- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index a1c281e93..f77326812 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -64,8 +64,12 @@ class FraudProcessorResponse { */ public function to_array(): array { return array( - 'avs_code' => $this->avs_code(), - 'cvv2_code' => $this->cvv_code(), + 'avs_code' => $this->avs_code(), + 'cvv2_code' => $this->cvv_code(), + // For backwards compatibility. + 'address_match' => $this->avs_code() === 'M' ? 'Y' : 'N', + 'postal_match' => $this->avs_code() === 'M' ? 'Y' : 'N', + 'cvv_match' => $this->cvv_code() === 'M' ? 'Y' : 'N', ); } @@ -77,36 +81,36 @@ class FraudProcessorResponse { * * @return string The AVS response code message. If the code is not found, an error message is returned. */ - public function get_avs_code_messages(): string { - if ( $this->avs_code() ) { + public function get_avs_code_message(): string { + if ( ! $this->avs_code() ) { return ''; } $messages = array( /* Visa, Mastercard, Discover, American Express */ - 'A' => __( 'A: Address - Address only (no ZIP code)', 'woocommerce-paypal-payments' ), - 'B' => __( 'B: International "A" - Address only (no ZIP code)', 'woocommerce-paypal-payments' ), - 'C' => __( 'C: International "N" - None. The transaction is declined.', 'woocommerce-paypal-payments' ), - 'D' => __( 'D: International "X" - Address and Postal Code', 'woocommerce-paypal-payments' ), - 'E' => __( 'E: Not allowed for MOTO (Internet/Phone) transactions - Not applicable. The transaction is declined.', 'woocommerce-paypal-payments' ), - 'F' => __( 'F: UK-specific "X" - Address and Postal Code', 'woocommerce-paypal-payments' ), - 'G' => __( 'G: Global Unavailable - Not applicable', 'woocommerce-paypal-payments' ), - 'I' => __( 'I: International Unavailable - Not applicable', 'woocommerce-paypal-payments' ), - 'M' => __( 'M: Address - Address and Postal Code', 'woocommerce-paypal-payments' ), - 'N' => __( 'N: No - None. The transaction is declined.', 'woocommerce-paypal-payments' ), - 'P' => __( 'P: Postal (International "Z") - Postal Code only (no Address)', 'woocommerce-paypal-payments' ), - 'R' => __( 'R: Retry - Not applicable', 'woocommerce-paypal-payments' ), - 'S' => __( 'S: Service not Supported - Not applicable', 'woocommerce-paypal-payments' ), - 'U' => __( 'U: Unavailable / Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), - 'W' => __( 'W: Whole ZIP - Nine-digit ZIP code (no Address)', 'woocommerce-paypal-payments' ), - 'X' => __( 'X: Exact match - Address and nine-digit ZIP code)', 'woocommerce-paypal-payments' ), - 'Y' => __( 'Y: Yes - Address and five-digit ZIP', 'woocommerce-paypal-payments' ), - 'Z' => __( 'Z: ZIP - Five-digit ZIP code (no Address)', 'woocommerce-paypal-payments' ), + 'A' => 'A: Address - Address only (no ZIP code)', + 'B' => 'B: International "A" - Address only (no ZIP code)', + 'C' => 'C: International "N" - None. The transaction is declined.', + 'D' => 'D: International "X" - Address and Postal Code', + 'E' => 'E: Not allowed for MOTO (Internet/Phone) transactions - Not applicable. The transaction is declined.', + 'F' => 'F: UK-specific "X" - Address and Postal Code', + 'G' => 'G: Global Unavailable - Not applicable', + 'I' => 'I: International Unavailable - Not applicable', + 'M' => 'M: Address - Address and Postal Code', + 'N' => 'N: No - None. The transaction is declined.', + 'P' => 'P: Postal (International "Z") - Postal Code only (no Address)', + 'R' => 'R: Retry - Not applicable', + 'S' => 'S: Service not Supported - Not applicable', + 'U' => 'U: Unavailable / Address not checked, or acquirer had no response. Service not available.', + 'W' => 'W: Whole ZIP - Nine-digit ZIP code (no Address)', + 'X' => 'X: Exact match - Address and nine-digit ZIP code)', + 'Y' => 'Y: Yes - Address and five-digit ZIP', + 'Z' => 'Z: ZIP - Five-digit ZIP code (no Address)', /* Maestro */ - '0' => __( '0: All the address information matched.', 'woocommerce-paypal-payments' ), - '1' => __( '1: None of the address information matched. The transaction is declined.', 'woocommerce-paypal-payments' ), - '2' => __( '2: Part of the address information matched.', 'woocommerce-paypal-payments' ), - '3' => __( '3: The merchant did not provide AVS information. Not processed.', 'woocommerce-paypal-payments' ), - '4' => __( '4: Address not checked, or acquirer had no response. Service not available.', 'woocommerce-paypal-payments' ), + '0' => '0: All the address information matched.', + '1' => '1: None of the address information matched. The transaction is declined.', + '2' => '2: Part of the address information matched.', + '3' => '3: The merchant did not provide AVS information. Not processed.', + '4' => '4: Address not checked, or acquirer had no response. Service not available.', ); /** @@ -115,8 +119,7 @@ class FraudProcessorResponse { * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ - /* translators: %s is fraud AVS code */ - return $messages[ $this->avs_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->avs_code() ); + return $messages[ $this->avs_code() ] ?? sprintf( '%s: Error', $this->avs_code() ); } /** @@ -126,26 +129,26 @@ class FraudProcessorResponse { * * @return string The descriptive message corresponding to the CVV2 code, or a formatted error message if the code is unrecognized. */ - public function get_cvv2_code_messages(): string { - if ( $this->cvv_code() ) { + public function get_cvv2_code_message(): string { + if ( ! $this->cvv_code() ) { return ''; } $messages = array( /* Visa, Mastercard, Discover, American Express */ - 'E' => __( 'E: Error - Unrecognized or Unknown response', 'woocommerce-paypal-payments' ), - 'I' => __( 'I: Invalid or Null', 'woocommerce-paypal-payments' ), - 'M' => __( 'M: Match or CSC', 'woocommerce-paypal-payments' ), - 'N' => __( 'N: No match', 'woocommerce-paypal-payments' ), - 'P' => __( 'P: Not processed', 'woocommerce-paypal-payments' ), - 'S' => __( 'S: Service not supported', 'woocommerce-paypal-payments' ), - 'U' => __( 'U: Unknown - Issuer is not certified', 'woocommerce-paypal-payments' ), - 'X' => __( 'X: No response / Service not available', 'woocommerce-paypal-payments' ), + 'E' => 'E: Error - Unrecognized or Unknown response', + 'I' => 'I: Invalid or Null', + 'M' => 'M: Match or CSC', + 'N' => 'N: No match', + 'P' => 'P: Not processed', + 'S' => 'S: Service not supported', + 'U' => 'U: Unknown - Issuer is not certified', + 'X' => 'X: No response / Service not available', /* Maestro */ - '0' => __( '0: Matched CVV2', 'woocommerce-paypal-payments' ), - '1' => __( '1: No match', 'woocommerce-paypal-payments' ), - '2' => __( '2: The merchant has not implemented CVV2 code handling', 'woocommerce-paypal-payments' ), - '3' => __( '3: Merchant has indicated that CVV2 is not present on card', 'woocommerce-paypal-payments' ), - '4' => __( '4: Service not available', 'woocommerce-paypal-payments' ), + '0' => '0: Matched CVV2', + '1' => '1: No match', + '2' => '2: The merchant has not implemented CVV2 code handling', + '3' => '3: Merchant has indicated that CVV2 is not present on card', + '4' => '4: Service not available', ); /** @@ -154,7 +157,6 @@ class FraudProcessorResponse { * @psalm-suppress PossiblyNullArrayOffset * @psalm-suppress PossiblyNullArgument */ - /* translators: %s is fraud CVV2 code */ - return $messages[ $this->cvv_code() ] ?? sprintf( __( '%s: Error', 'woocommerce-paypal-payments' ), $this->cvv_code() ); + return $messages[ $this->cvv_code() ] ?? sprintf( '%s: Error', $this->cvv_code() ); } } diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index c909e5981..c730136ab 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -99,23 +99,22 @@ trait CreditCardOrderInfoHandlingTrait { $card_brand = $payment_source->properties()->brand ?? __( 'N/A', 'woocommerce-paypal-payments' ); $card_last_digits = $payment_source->properties()->last_digits ?? __( 'N/A', 'woocommerce-paypal-payments' ); - $response_order_note_title = __( 'Card decline errors', 'woocommerce-paypal-payments' ); + $response_order_note_title = __( 'PayPal Advanced Card Processing Verification:', 'woocommerce-paypal-payments' ); /* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */ $response_order_note_format = __( '%1$s %2$s', 'woocommerce-paypal-payments' ); $response_order_note_result_format = '
  • %1$s
  • %2$s
  • %3$s
  • -
  • %3$s
'; $response_order_note_result = sprintf( $response_order_note_result_format, /* translators: %1$s is card brand and %2$s card last 4 digits */ sprintf( __( 'Card: %1$s (%2$s)', 'woocommerce-paypal-payments' ), $card_brand, $card_last_digits ), /* translators: %s is fraud AVS message */ - sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), $fraud->get_avs_code_messages() ), + sprintf( __( 'AVS: %s', 'woocommerce-paypal-payments' ), $fraud->get_avs_code_message() ), /* translators: %s is fraud CVV message */ - sprintf( __( 'CVV: %s', 'woocommerce-paypal-payments' ), $fraud->get_cvv2_code_messages() ), + sprintf( __( 'CVV: %s', 'woocommerce-paypal-payments' ), $fraud->get_cvv2_code_message() ), ); $response_order_note = sprintf( $response_order_note_format, From 9c447835ab12c956dd51718ccd52e5f0392f3b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 15:31:12 +0100 Subject: [PATCH 101/169] Fix negative unit amount adjustments in item sanitization --- modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php index 7cb0c048f..1d222f605 100644 --- a/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php +++ b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php @@ -178,6 +178,11 @@ class PurchaseUnitSanitizer { // Get a more intelligent adjustment mechanism. $increment = ( new MoneyFormatter() )->minimum_increment( $item['unit_amount']['currency_code'] ); + // not floor items that will be negative then. + if ( (float) $item['unit_amount']['value'] < $increment ) { + continue; + } + $this->purchase_unit['items'][ $index ]['unit_amount'] = ( new Money( ( (float) $item['unit_amount']['value'] ) - $increment, $item['unit_amount']['currency_code'] From ae275210ca7e525b2389bb51cd32a90238d88c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 11 Dec 2024 15:50:12 +0100 Subject: [PATCH 102/169] fix null return type --- modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php index f77326812..baecabf73 100644 --- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php +++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php @@ -44,7 +44,7 @@ class FraudProcessorResponse { * * @return string */ - public function avs_code(): ?string { + public function avs_code(): string { return $this->avs_code; } @@ -53,7 +53,7 @@ class FraudProcessorResponse { * * @return string */ - public function cvv_code(): ?string { + public function cvv_code(): string { return $this->cvv2_code; } From 825e1d0f7353da6a26fa7c93938a8f684ed2a2fb Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 16:56:05 +0100 Subject: [PATCH 103/169] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Undo=20last=20comm?= =?UTF-8?q?it?= 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 104/169] =?UTF-8?q?=F0=9F=8E=A8=20Minor=20code=20style=20c?= =?UTF-8?q?hange?= 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 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; */ 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 @@ class Settings implements ContainerInterface { /** * 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 @@ class Settings implements ContainerInterface { /** * 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 @@ class Settings implements ContainerInterface { /** * 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 @@ class Settings implements ContainerInterface { } $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 105/169] =?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 @@ class SettingsMapHelper { /** * 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 @@ class SettingsMapHelper { /** * 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 106/169] =?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 @@ declare( strict_types = 1 ); 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 @@ class SettingsMapHelper { } /** - * 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 107/169] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Add=20mapping-cach?= =?UTF-8?q?e=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 @@ class SettingsMapHelper { * @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(); + + 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(), + ); } } - - return false; } } From dc7e318a1a761aaf0dfca76679958932a0e614c4 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 11 Dec 2024 21:08:13 +0100 Subject: [PATCH 108/169] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Add=20cache=20for?= =?UTF-8?q?=20=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 @@ class SettingsMapHelper { * @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(); + $mapping = $this->key_to_model[ $old_key ]; + $model_id = spl_object_id( $mapping['model'] ); - if ( ! empty( $new_settings[ $mapped_key ] ) ) { - return $new_settings[ $mapped_key ]; - } - } - - return null; + return $this->get_cached_model_value( $model_id, $mapping['new_key'], $mapping['model'] ); } /** @@ -78,6 +81,23 @@ class SettingsMapHelper { 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 109/169] =?UTF-8?q?=F0=9F=A5=85=20Prevent=20invalid=20sett?= =?UTF-8?q?ings-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 @@ declare( strict_types = 1 ); 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 110/169] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Revert=20type=20an?= =?UTF-8?q?notations?= 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 @@ class Settings implements ContainerInterface { * * @return mixed */ - public function get( string $id ) { + public function get( $id ) { if ( ! $this->has( $id ) ) { throw new NotFoundException(); } @@ -119,7 +119,7 @@ class Settings implements ContainerInterface { * * @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 @@ class Settings implements ContainerInterface { * @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 @@ class Settings implements ContainerInterface { /** * 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 @@ class Settings implements ContainerInterface { $this->settings[ $key ] = $value; } + return true; } } From 5e78085a7ae0f7a695245c692637ff3c05837e0e Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Thu, 12 Dec 2024 06:04:03 +0100 Subject: [PATCH 111/169] Copy endpoint --- .../src/Endpoint/WebhookSettingsEndpoint.php | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 modules/ppcp-settings/src/Endpoint/WebhookSettingsEndpoint.php diff --git a/modules/ppcp-settings/src/Endpoint/WebhookSettingsEndpoint.php b/modules/ppcp-settings/src/Endpoint/WebhookSettingsEndpoint.php new file mode 100644 index 000000000..328ff54c4 --- /dev/null +++ b/modules/ppcp-settings/src/Endpoint/WebhookSettingsEndpoint.php @@ -0,0 +1,167 @@ + array( + 'js_name' => 'completed', + 'sanitize' => 'to_boolean', + ), + 'step' => array( + 'js_name' => 'step', + 'sanitize' => 'to_number', + ), + 'is_casual_seller' => array( + 'js_name' => 'isCasualSeller', + 'sanitize' => 'to_boolean', + ), + 'are_optional_payment_methods_enabled' => array( + 'js_name' => 'areOptionalPaymentMethodsEnabled', + 'sanitize' => 'to_boolean', + ), + 'products' => array( + 'js_name' => 'products', + ), + ); + + /** + * Map the internal flags to JS names. + * + * @var array + */ + private array $flag_map = array( + 'can_use_casual_selling' => array( + 'js_name' => 'canUseCasualSelling', + ), + 'can_use_vaulting' => array( + 'js_name' => 'canUseVaulting', + ), + 'can_use_card_payments' => array( + 'js_name' => 'canUseCardPayments', + ), + 'can_use_subscriptions' => array( + 'js_name' => 'canUseSubscriptions', + ), + ); + + /** + * Constructor. + * + * @param OnboardingProfile $profile The settings instance. + */ + public function __construct( OnboardingProfile $profile ) { + $this->profile = $profile; + + $this->field_map['products']['sanitize'] = fn( $list ) => array_map( 'sanitize_text_field', $list ); + } + + /** + * Configure REST API routes. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_details' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_details' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + ) + ); + } + + /** + * Returns all details of the current onboarding wizard progress. + * + * @return WP_REST_Response The current state of the onboarding wizard. + */ + public function get_details() : WP_REST_Response { + $js_data = $this->sanitize_for_javascript( + $this->profile->to_array(), + $this->field_map + ); + + $js_flags = $this->sanitize_for_javascript( + $this->profile->get_flags(), + $this->flag_map + ); + + return $this->return_success( + $js_data, + array( + 'flags' => $js_flags, + ) + ); + } + + /** + * Updates onboarding details based on the request. + * + * @param WP_REST_Request $request Full data about the request. + * + * @return WP_REST_Response The updated state of the onboarding wizard. + */ + public function update_details( WP_REST_Request $request ) : WP_REST_Response { + $wp_data = $this->sanitize_for_wordpress( + $request->get_params(), + $this->field_map + ); + + $this->profile->from_array( $wp_data ); + $this->profile->save(); + + return $this->get_details(); + } +} From 169c184e57e4bbb105bb810678f14792046470f0 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Thu, 12 Dec 2024 06:26:07 +0100 Subject: [PATCH 112/169] 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 113/169] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20default=20order?= =?UTF-8?q?=20in=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 @@ class SettingsMapHelper { /** * 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 947f20a2a5e3543293daf7ae55f851f69fdd0c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Thu, 12 Dec 2024 16:03:32 +0100 Subject: [PATCH 114/169] check if axo is enabled or not for buttons --- modules/ppcp-axo-block/src/AxoBlockModule.php | 5 ++++- modules/ppcp-axo/services.php | 4 +++- modules/ppcp-axo/src/AxoModule.php | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo-block/src/AxoBlockModule.php b/modules/ppcp-axo-block/src/AxoBlockModule.php index c8216bf62..af94976a3 100644 --- a/modules/ppcp-axo-block/src/AxoBlockModule.php +++ b/modules/ppcp-axo-block/src/AxoBlockModule.php @@ -92,7 +92,10 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule */ add_filter( 'woocommerce_paypal_payments_sdk_components_hook', - function( $components ) { + function( $components ) use ( $c ) { + if ( ! $c->has( 'axo.available' ) || ! $c->get( 'axo.available' ) ) { + return $components; + } $components[] = 'fastlane'; return $components; } diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 121b17805..ca427700d 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -44,7 +44,9 @@ return array( // If AXO is configured and onboarded. 'axo.available' => static function ( ContainerInterface $container ): bool { - return true; + $settings = $container->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + return $settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' ); }, 'axo.url' => static function ( ContainerInterface $container ): string { diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3ac0ff157..8be0f3c22 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -246,7 +246,10 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { */ add_filter( 'woocommerce_paypal_payments_sdk_components_hook', - function( $components ) { + function( $components ) use ( $c ) { + if ( ! $c->has( 'axo.available' ) || ! $c->get( 'axo.available' ) ) { + return $components; + } $components[] = 'fastlane'; return $components; } From 3b85c5037ff98a218e43dfb4d552d606ebd3bac3 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 12 Dec 2024 19:09:14 +0400 Subject: [PATCH 115/169] Don't show buttons if cart contains free trial product and the stroe is not eligible for saving payment methods. --- modules/ppcp-blocks/resources/js/checkout-block.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index fc7720f93..03c60745a 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -59,6 +59,14 @@ if ( cartHasSubscriptionProducts( config.scriptData ) ) { blockEnabled = false; } + // Don't show buttons if cart contains free trial product and the stroe is not eligible for saving payment methods. + if ( + ! config.scriptData.vault_v3_enabled && + config.scriptData.is_free_trial_cart + ) { + blockEnabled = false; + } + features.push( 'subscriptions' ); } From e59ae23c43c67ec64aae93519e8b9962bcbb4d2c Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 12 Dec 2024 16:55:19 +0100 Subject: [PATCH 116/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Replace=20generate?= =?UTF-8?q?PriceText=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 117/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20price=20?= =?UTF-8?q?description=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 118/169] =?UTF-8?q?=E2=9C=A8=20Introduce=20date-variable?= =?UTF-8?q?=20in=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 119/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20country=20c?= =?UTF-8?q?heck=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 120/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20country=20l?= =?UTF-8?q?ogic=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 121/169] =?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 122/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Remove=20footnote-?= =?UTF-8?q?number=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 123/169] =?UTF-8?q?=E2=9C=A8=20Introduce=20price-matrix=20?= =?UTF-8?q?and=20currency-formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReusableComponents/PricingTitleBadge.js | 22 +++++++--- .../resources/js/utils/countryPriceInfo.js | 40 +++++++++++-------- .../resources/js/utils/formatPrice.js | 34 ++++++++++++++++ 3 files changed, 74 insertions(+), 22 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 { countryPriceInfo } from '../../utils/countryPriceInfo'; -import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; import { CommonHooks } from '../../data'; +import { countryPriceInfo } from '../../utils/countryPriceInfo'; +import { formatPrice } from '../../utils/formatPrice'; +import TitleBadge, { TITLE_BADGE_INFO } from './TitleBadge'; + +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 }`; +}; From ec1d41261a805baba172ad9d538cc2a5ccd6ff25 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 13 Dec 2024 12:31:20 +0100 Subject: [PATCH 124/169] =?UTF-8?q?=E2=9C=A8=20Add=20own=20integration=20o?= =?UTF-8?q?f=20the=20=E2=80=9Cnavigation=E2=80=9D=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/utils/navigation.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 modules/ppcp-settings/resources/js/utils/navigation.js diff --git a/modules/ppcp-settings/resources/js/utils/navigation.js b/modules/ppcp-settings/resources/js/utils/navigation.js new file mode 100644 index 000000000..f2106bc1d --- /dev/null +++ b/modules/ppcp-settings/resources/js/utils/navigation.js @@ -0,0 +1,39 @@ +import { addQueryArgs } from '@wordpress/url'; + +const getLocation = () => window.location; + +const pushHistory = ( path ) => window.history.pushState( { path }, '', path ); + +/** + * Get the current path from the browser. + * + * @return {string} Current path. + */ +export const getPath = () => getLocation().pathname; + +/** + * Get the current query string, parsed into an object, from history. + * + * @return {Object} Current query object, defaults to empty object. + */ +export const getQuery = () => + Object.fromEntries( new URLSearchParams( getLocation().search ) ); + +/** + * Updates the query parameters of the current page. + * + * @param {Object} query Object of params to be updated. + * @throws {TypeError} If the query is not an object. + */ +export const updateQueryString = ( query ) => + pushHistory( getNewPath( query ) ); + +/** + * Return a URL with set query parameters. + * + * @param {Object} query Object of params to be updated. + * @param {string} basePath Optional. Define the path for the new URL. + * @return {string} Updated URL merging query params into existing params. + */ +export const getNewPath = ( query, basePath = getPath() ) => + addQueryArgs( basePath, { ...getQuery(), ...query } ); From 6f9701504126bedff39afd1b5fc38a9c14eedada Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 13 Dec 2024 12:40:09 +0100 Subject: [PATCH 125/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20Replace=20exter?= =?UTF-8?q?nal=20module=20with=20own=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/ReusableComponents/TabNavigation.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js index 4c5d40675..e22711255 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js @@ -1,6 +1,7 @@ import { useCallback, useEffect, useState } from '@wordpress/element'; import { TabPanel } from '@wordpress/components'; -import { getQuery, updateQueryString } from '@woocommerce/navigation'; + +import { getQuery, updateQueryString } from '../../utils/navigation'; const TabNavigation = ( { tabs } ) => { const { panel } = getQuery(); @@ -30,7 +31,7 @@ const TabNavigation = ( { tabs } ) => { ); useEffect( () => { - updateQueryString( { panel: activePanel }, '/', getQuery() ); + updateQueryString( { panel: activePanel } ); }, [ activePanel ] ); return ( From 142e22452ffe1df76f035e0382acb970f5500828 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Fri, 13 Dec 2024 12:45:07 +0100 Subject: [PATCH 126/169] =?UTF-8?q?=E2=9E=96=20Remove=20deprecated=20?= =?UTF-8?q?=E2=80=9Cnavigation=E2=80=9D=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/package.json | 5 +- modules/ppcp-settings/yarn.lock | 962 +---------------------------- 2 files changed, 27 insertions(+), 940 deletions(-) diff --git a/modules/ppcp-settings/package.json b/modules/ppcp-settings/package.json index 0a1391b88..47e69347c 100644 --- a/modules/ppcp-settings/package.json +++ b/modules/ppcp-settings/package.json @@ -7,14 +7,13 @@ "build": "wp-scripts build --webpack-src-dir=resources/js --output-path=assets" }, "devDependencies": { - "@woocommerce/navigation": "~8.1.0", "@wordpress/data": "^10.10.0", "@wordpress/data-controls": "^4.10.0", - "@wordpress/scripts": "^30.3.0" + "@wordpress/scripts": "^30.3.0", + "classnames": "^2.5.1" }, "dependencies": { "@paypal/react-paypal-js": "^8.7.0", - "@woocommerce/settings": "^1.0.0", "react-select": "^5.8.3" } } diff --git a/modules/ppcp-settings/yarn.lock b/modules/ppcp-settings/yarn.lock index a3b2a6adb..6b623d53a 100644 --- a/modules/ppcp-settings/yarn.lock +++ b/modules/ppcp-settings/yarn.lock @@ -1117,14 +1117,6 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.9" "@babel/plugin-transform-typescript" "^7.25.9" -"@babel/runtime-corejs2@7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2" - integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.2" - "@babel/runtime@7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" @@ -1132,7 +1124,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1221,7 +1213,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0", "@emotion/cache@^11.7.1": +"@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": version "11.13.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== @@ -1232,47 +1224,17 @@ "@emotion/weak-memoize" "^0.4.0" stylis "4.2.0" -"@emotion/css@^11.7.1": - version "11.13.4" - resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.13.4.tgz#a5128e34a23f5e2c891970b8ec98a60c5a2395e1" - integrity sha512-CthbOD5EBw+iN0rfM96Tuv5kaZN4nxPyYDvGUs0bc7wZBBiU/0mse+l+0O9RshW2d+v5HH1cme+BAbLJ/3Folw== - dependencies: - "@emotion/babel-plugin" "^11.12.0" - "@emotion/cache" "^11.13.0" - "@emotion/serialize" "^1.3.0" - "@emotion/sheet" "^1.4.0" - "@emotion/utils" "^1.4.0" - "@emotion/hash@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== -"@emotion/is-prop-valid@^0.8.2": - version "0.8.8" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" - integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== - dependencies: - "@emotion/memoize" "0.7.4" - -"@emotion/is-prop-valid@^1.3.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240" - integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== - dependencies: - "@emotion/memoize" "^0.9.0" - -"@emotion/memoize@0.7.4": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" - integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== - "@emotion/memoize@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@^11.7.1", "@emotion/react@^11.8.1": +"@emotion/react@^11.8.1": version "11.13.3" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== @@ -1286,7 +1248,7 @@ "@emotion/weak-memoize" "^0.4.0" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.0.2", "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0", "@emotion/serialize@^1.3.1": +"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1": version "1.3.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a" integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== @@ -1302,18 +1264,6 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.6.0": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.13.0.tgz#633fd700db701472c7a5dbef54d6f9834e9fb190" - integrity sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.12.0" - "@emotion/is-prop-valid" "^1.3.0" - "@emotion/serialize" "^1.3.0" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" - "@emotion/utils" "^1.4.0" - "@emotion/unitless@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" @@ -1324,11 +1274,6 @@ resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== -"@emotion/utils@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" - integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== - "@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad" @@ -1380,11 +1325,6 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== -"@floating-ui/core@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.6.2.tgz#f2813f0e5f3d5ed7af5029e1a082203dadf02b7d" - integrity sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg== - "@floating-ui/core@^1.6.0": version "1.6.8" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" @@ -1392,13 +1332,6 @@ dependencies: "@floating-ui/utils" "^0.2.8" -"@floating-ui/dom@^0.4.5": - version "0.4.5" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-0.4.5.tgz#2e88d16646119cc67d44683f75ee99840475bbfa" - integrity sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw== - dependencies: - "@floating-ui/core" "^0.6.2" - "@floating-ui/dom@^1.0.1": version "1.6.12" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.12.tgz#6333dcb5a8ead3b2bf82f33d6bc410e95f54e556" @@ -1407,14 +1340,6 @@ "@floating-ui/core" "^1.6.0" "@floating-ui/utils" "^0.2.8" -"@floating-ui/react-dom@0.6.3": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-0.6.3.tgz#7b64cfd4fd12e4a0515dbf1b2be16e48c9a06c5a" - integrity sha512-hC+pS5D6AgS2wWjbmSQ6UR6Kpy+drvWGJIri6e1EDGADTPsCaa4KzCgmCczHrQeInx9tqs81EyDmbKJYY2swKg== - dependencies: - "@floating-ui/dom" "^0.4.5" - use-isomorphic-layout-effect "^1.1.1" - "@floating-ui/utils@^0.2.8": version "0.2.8" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" @@ -1704,59 +1629,6 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== -"@motionone/animation@^10.12.0": - version "10.18.0" - resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.18.0.tgz#868d00b447191816d5d5cf24b1cafa144017922b" - integrity sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw== - dependencies: - "@motionone/easing" "^10.18.0" - "@motionone/types" "^10.17.1" - "@motionone/utils" "^10.18.0" - tslib "^2.3.1" - -"@motionone/dom@10.12.0": - version "10.12.0" - resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed" - integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw== - dependencies: - "@motionone/animation" "^10.12.0" - "@motionone/generators" "^10.12.0" - "@motionone/types" "^10.12.0" - "@motionone/utils" "^10.12.0" - hey-listen "^1.0.8" - tslib "^2.3.1" - -"@motionone/easing@^10.18.0": - version "10.18.0" - resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.18.0.tgz#7b82f6010dfee3a1bb0ee83abfbaff6edae0c708" - integrity sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg== - dependencies: - "@motionone/utils" "^10.18.0" - tslib "^2.3.1" - -"@motionone/generators@^10.12.0": - version "10.18.0" - resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.18.0.tgz#fe09ab5cfa0fb9a8884097feb7eb60abeb600762" - integrity sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg== - dependencies: - "@motionone/types" "^10.17.1" - "@motionone/utils" "^10.18.0" - tslib "^2.3.1" - -"@motionone/types@^10.12.0", "@motionone/types@^10.17.1": - version "10.17.1" - resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.17.1.tgz#cf487badbbdc9da0c2cb86ffc1e5d11147c6e6fb" - integrity sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A== - -"@motionone/utils@^10.12.0", "@motionone/utils@^10.18.0": - version "10.18.0" - resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.18.0.tgz#a59ff8932ed9009624bca07c56b28ef2bb2f885e" - integrity sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw== - dependencies: - "@motionone/types" "^10.17.1" - hey-listen "^1.0.8" - tslib "^2.3.1" - "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -1919,11 +1791,6 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== -"@popperjs/core@^2.5.4": - version "2.11.8" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" - integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== - "@puppeteer/browsers@1.4.6": version "1.4.6" resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.4.6.tgz#1f70fd23d5d2ccce9d29b038e5039d7a1049ca77" @@ -2456,13 +2323,6 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^17.0.11": - version "17.0.25" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" - integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== - dependencies: - "@types/react" "^17" - "@types/react-dom@^18.2.25": version "18.3.1" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" @@ -2485,25 +2345,11 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^17", "@types/react@^17.0.37": - version "17.0.83" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.83.tgz#b477c56387b74279281149dcf5ba2a1e2216d131" - integrity sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "^0.16" - csstype "^3.0.2" - "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/scheduler@^0.16": - version "0.16.8" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/semver@^7.3.12", "@types/semver@^7.5.0": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" @@ -2753,18 +2599,6 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@use-gesture/core@10.3.1": - version "10.3.1" - resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.3.1.tgz#976c9421e905f0079d49822cfd5c2e56b808fc56" - integrity sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw== - -"@use-gesture/react@^10.2.6": - version "10.3.1" - resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.3.1.tgz#17a743a894d9bd9a0d1980c618f37f0164469867" - integrity sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g== - dependencies: - "@use-gesture/core" "10.3.1" - "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" @@ -2901,37 +2735,6 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@woocommerce/navigation@~8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@woocommerce/navigation/-/navigation-8.1.0.tgz#dc0183e61d0fb139844f5471839b723dbb289b4a" - integrity sha512-Ifl8IYRLYlbxk6RNuuVorMaCoOs8aFWEo8oSU++SqFfyjPi893Nuk6NJYVvAVhxFdwPfw9RptvQ/q8sIusPihA== - dependencies: - "@wordpress/api-fetch" "^6.0.1" - "@wordpress/components" "^19.5.0" - "@wordpress/compose" "^5.1.2" - "@wordpress/element" "^4.1.1" - "@wordpress/hooks" "^3.5.0" - "@wordpress/notices" "^3.3.2" - "@wordpress/url" "^3.4.1" - history "^5.3.0" - qs "^6.10.3" - -"@woocommerce/settings@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@woocommerce/settings/-/settings-1.0.0.tgz#c35f85d1c9c03f68c77541bf323a77fa31720312" - integrity sha512-BjrT56Cz8XTRHw2JNPmANRkYh2rzdF33wOa56lah1qb/MjHUKuVJ0PTSZ19S5Trb92IkxfcIVB26CSdxXnf5Og== - dependencies: - "@babel/runtime-corejs2" "7.5.5" - -"@wordpress/a11y@^3.15.0", "@wordpress/a11y@^3.22.0", "@wordpress/a11y@^3.31.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/a11y/-/a11y-3.58.0.tgz#8e8853709061b3042ca6cb9ac1bc6a5ec2bc9232" - integrity sha512-7NnJKl4+pxP6kV/jvXaJcZZCGzW7zaj6YeMnyjUd96cH4ta1ykBIveWgejerFOGsbK+88FnStcxSFj+dbDXs/w== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/dom-ready" "^3.58.0" - "@wordpress/i18n" "^4.58.0" - "@wordpress/api-fetch@*": version "7.11.0" resolved "https://registry.yarnpkg.com/@wordpress/api-fetch/-/api-fetch-7.11.0.tgz#83c7158cb45c5b2390be0383c680a797a5e72452" @@ -2941,15 +2744,6 @@ "@wordpress/i18n" "*" "@wordpress/url" "*" -"@wordpress/api-fetch@^6.0.1": - version "6.55.0" - resolved "https://registry.yarnpkg.com/@wordpress/api-fetch/-/api-fetch-6.55.0.tgz#a28883cfa3a31590838cb1f0ae863d7c3d391499" - integrity sha512-1HrCUsJdeRY5Y0IjplotINwqMRO81e7O7VhBScuKk7iOuDm/E1ioKv2uLGnPNWziYu+Zf025byxOqVzXDyM2gw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/i18n" "^4.58.0" - "@wordpress/url" "^3.59.0" - "@wordpress/babel-preset-default@*": version "8.11.0" resolved "https://registry.yarnpkg.com/@wordpress/babel-preset-default/-/babel-preset-default-8.11.0.tgz#603e773093729542a893c91faf9b58b133bc2e0a" @@ -2977,52 +2771,6 @@ resolved "https://registry.yarnpkg.com/@wordpress/browserslist-config/-/browserslist-config-6.11.0.tgz#4637f0f1336309e519e858347480c01bf2fa8c83" integrity sha512-wUDbJ3x7c8iMZLtwo+7VlWZ/vDc47PDW2eSAKW18RrQBSTdaNmWi4qiyFYi7Ye2XkyfUd2gp71MTJjZi6n/V2A== -"@wordpress/components@^19.5.0": - version "19.17.0" - resolved "https://registry.yarnpkg.com/@wordpress/components/-/components-19.17.0.tgz#c15b1467aaa7056d3fbd74c04644074ef43d49de" - integrity sha512-6FsLq1WS924fjZjRGSuen3Tzaa4mEWRtCTHM2JS5eE5+rnuhddiHNNgvw26IZCwhQYQwIvIKq9m9in0F0fSOzg== - dependencies: - "@babel/runtime" "^7.16.0" - "@emotion/cache" "^11.7.1" - "@emotion/css" "^11.7.1" - "@emotion/react" "^11.7.1" - "@emotion/serialize" "^1.0.2" - "@emotion/styled" "^11.6.0" - "@emotion/utils" "1.0.0" - "@floating-ui/react-dom" "0.6.3" - "@use-gesture/react" "^10.2.6" - "@wordpress/a11y" "^3.15.0" - "@wordpress/compose" "^5.13.0" - "@wordpress/date" "^4.15.0" - "@wordpress/deprecated" "^3.15.0" - "@wordpress/dom" "^3.15.0" - "@wordpress/element" "^4.13.0" - "@wordpress/escape-html" "^2.15.0" - "@wordpress/hooks" "^3.15.0" - "@wordpress/i18n" "^4.15.0" - "@wordpress/icons" "^9.6.0" - "@wordpress/is-shallow-equal" "^4.15.0" - "@wordpress/keycodes" "^3.15.0" - "@wordpress/primitives" "^3.13.0" - "@wordpress/rich-text" "^5.13.0" - "@wordpress/warning" "^2.15.0" - classnames "^2.3.1" - colord "^2.7.0" - dom-scroll-into-view "^1.2.1" - downshift "^6.0.15" - framer-motion "^6.2.8" - gradient-parser "^0.1.5" - highlight-words-core "^1.2.2" - lodash "^4.17.21" - memize "^1.1.0" - moment "^2.26.0" - re-resizable "^6.4.0" - react-colorful "^5.3.1" - react-dates "^21.8.0" - reakit "^1.3.8" - remove-accents "^0.4.2" - uuid "^8.3.0" - "@wordpress/compose@*": version "7.11.0" resolved "https://registry.yarnpkg.com/@wordpress/compose/-/compose-7.11.0.tgz#a57702e9529aa842ce45968edb286cc0ef22299e" @@ -3042,43 +2790,6 @@ mousetrap "^1.6.5" use-memo-one "^1.1.1" -"@wordpress/compose@^5.1.2", "@wordpress/compose@^5.13.0", "@wordpress/compose@^5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@wordpress/compose/-/compose-5.20.0.tgz#e5c5181bca058f73b13fb7007d96f800b6501f71" - integrity sha512-IcmXeAIgZoJUFIO3bxBpPYfAre41H6zxQTC5N6nqhGqpISvbO1SsAIikd6B4AoSHUZmYV5UoTxk9kECqZZGVOw== - dependencies: - "@babel/runtime" "^7.16.0" - "@types/mousetrap" "^1.6.8" - "@wordpress/deprecated" "^3.22.0" - "@wordpress/dom" "^3.22.0" - "@wordpress/element" "^4.20.0" - "@wordpress/is-shallow-equal" "^4.22.0" - "@wordpress/keycodes" "^3.22.0" - "@wordpress/priority-queue" "^2.22.0" - change-case "^4.1.2" - clipboard "^2.0.8" - mousetrap "^1.6.5" - use-memo-one "^1.1.1" - -"@wordpress/compose@^6.35.0": - version "6.35.0" - resolved "https://registry.yarnpkg.com/@wordpress/compose/-/compose-6.35.0.tgz#411a1929bb28102cf4c508a13dc4d46812bbc871" - integrity sha512-PfruhCxxxJokDQHc2YBgerEiHV7BIxQk9g5vU4/f9X/0PBQWUTuxOzSFcAba03vnjfAgtPTSMp50T50hcJwXfA== - dependencies: - "@babel/runtime" "^7.16.0" - "@types/mousetrap" "^1.6.8" - "@wordpress/deprecated" "^3.58.0" - "@wordpress/dom" "^3.58.0" - "@wordpress/element" "^5.35.0" - "@wordpress/is-shallow-equal" "^4.58.0" - "@wordpress/keycodes" "^3.58.0" - "@wordpress/priority-queue" "^2.58.0" - "@wordpress/undo-manager" "^0.18.0" - change-case "^4.1.2" - clipboard "^2.0.11" - mousetrap "^1.6.5" - use-memo-one "^1.1.1" - "@wordpress/data-controls@^4.10.0": version "4.11.0" resolved "https://registry.yarnpkg.com/@wordpress/data-controls/-/data-controls-4.11.0.tgz#48894aac92e5d517eb729f718d5861bd28cd7128" @@ -3110,57 +2821,6 @@ rememo "^4.0.2" use-memo-one "^1.1.1" -"@wordpress/data@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@wordpress/data/-/data-7.6.0.tgz#16e5d03653e527baeb00607d8c9cdacbb6c59bcc" - integrity sha512-Og+oinEpJzd2rI4cFQGJBtSNzSVEa1sDWje1dYc3Jm7t2/NpkGk/YXn0PlVhkakA7YCGBy2OhX122flgZBuaBw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/compose" "^5.20.0" - "@wordpress/deprecated" "^3.22.0" - "@wordpress/element" "^4.20.0" - "@wordpress/is-shallow-equal" "^4.22.0" - "@wordpress/priority-queue" "^2.22.0" - "@wordpress/redux-routine" "^4.22.0" - equivalent-key-map "^0.2.2" - is-plain-object "^5.0.0" - is-promise "^4.0.0" - lodash "^4.17.21" - redux "^4.1.2" - turbo-combine-reducers "^1.0.2" - use-memo-one "^1.1.1" - -"@wordpress/data@^9.1.0": - version "9.28.0" - resolved "https://registry.yarnpkg.com/@wordpress/data/-/data-9.28.0.tgz#64efd691384ba26faa1b460549fad9a237728818" - integrity sha512-EDPpZdkngdoW7EMzPpGj0BmNcr7syJO67pgTODtN/4XFIdYL2RKzFyn3nlLBKhX17UsE/ALq9WdijacH4QJ9qw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/compose" "^6.35.0" - "@wordpress/deprecated" "^3.58.0" - "@wordpress/element" "^5.35.0" - "@wordpress/is-shallow-equal" "^4.58.0" - "@wordpress/priority-queue" "^2.58.0" - "@wordpress/private-apis" "^0.40.0" - "@wordpress/redux-routine" "^4.58.0" - deepmerge "^4.3.0" - equivalent-key-map "^0.2.2" - is-plain-object "^5.0.0" - is-promise "^4.0.0" - redux "^4.1.2" - rememo "^4.0.2" - use-memo-one "^1.1.1" - -"@wordpress/date@^4.15.0": - version "4.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/date/-/date-4.58.0.tgz#6803e0bde8e8ccb62ebf57554f9543a14cc4433c" - integrity sha512-yFT7DU0H9W0lsDytMaVMmjho08X1LeBMIQMppxdtKB04Ujx58hVh7gtunOsstUQ7pVg23nE2eLaVfx5JOdjzAw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/deprecated" "^3.58.0" - moment "^2.29.4" - moment-timezone "^0.5.40" - "@wordpress/dependency-extraction-webpack-plugin@*": version "6.11.0" resolved "https://registry.yarnpkg.com/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-6.11.0.tgz#11ad1ab4700f33c1e80d7b8c2a81a4690afc06ad" @@ -3176,21 +2836,6 @@ "@babel/runtime" "7.25.7" "@wordpress/hooks" "*" -"@wordpress/deprecated@^3.15.0", "@wordpress/deprecated@^3.22.0", "@wordpress/deprecated@^3.58.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/deprecated/-/deprecated-3.58.0.tgz#c8b9442167bc20aef4888af4a4d081b4553adb4c" - integrity sha512-knweE2lLEUxWRr6A48sHiO0ww5pPybGe2NVIZVq/y7EaYCMdpy6gYA0ZdVqMKZvtxKKqicJfwigcn+hinsTvUQ== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/hooks" "^3.58.0" - -"@wordpress/dom-ready@^3.58.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/dom-ready/-/dom-ready-3.58.0.tgz#73453c803638438e10c003f68ce9ebc27138335f" - integrity sha512-sDgRPjNBToRKgYrpwvMRv2Yc7/17+sp8hm/rRnbubwb+d/DbGkK4Tc/r4sNLSZCqUAtcBXq9uk1lzvhge3QUSg== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/dom@*": version "4.11.0" resolved "https://registry.yarnpkg.com/@wordpress/dom/-/dom-4.11.0.tgz#c4cc6bdc6374112139c305a642a46ce1995e88a9" @@ -3199,14 +2844,6 @@ "@babel/runtime" "7.25.7" "@wordpress/deprecated" "*" -"@wordpress/dom@^3.15.0", "@wordpress/dom@^3.22.0", "@wordpress/dom@^3.58.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/dom/-/dom-3.58.0.tgz#c9afe87ce29d00a2baecfcf61a284232ce821612" - integrity sha512-t3xSr/nqekj2qwUGRAqSeGx6116JOBxzI+VBiUfZrjGEnuyKdLelXDEeYtcwbb7etMkj/6F60/NB7GTl5IwizQ== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/deprecated" "^3.58.0" - "@wordpress/e2e-test-utils-playwright@*": version "1.11.0" resolved "https://registry.yarnpkg.com/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.11.0.tgz#00dee8a1d945ecf9354a3f5eb6f56d4dfd4f38c5" @@ -3233,34 +2870,6 @@ react "^18.3.0" react-dom "^18.3.0" -"@wordpress/element@^4.1.1", "@wordpress/element@^4.13.0", "@wordpress/element@^4.20.0": - version "4.20.0" - resolved "https://registry.yarnpkg.com/@wordpress/element/-/element-4.20.0.tgz#d78499521cbb471b97e011a81ec6236daeac24ad" - integrity sha512-Ou7EoGtGe4FUL6fKALINXJLKoSfyWTBJzkJfN2HzSgM1wira9EuWahl8MQN0HAUaWeOoDqMKPvnglfS+kC8JLA== - dependencies: - "@babel/runtime" "^7.16.0" - "@types/react" "^17.0.37" - "@types/react-dom" "^17.0.11" - "@wordpress/escape-html" "^2.22.0" - change-case "^4.1.2" - is-plain-object "^5.0.0" - react "^17.0.2" - react-dom "^17.0.2" - -"@wordpress/element@^5.35.0": - version "5.35.0" - resolved "https://registry.yarnpkg.com/@wordpress/element/-/element-5.35.0.tgz#c0629200aa5f3644e921fc569cb53a8fb470af34" - integrity sha512-puswpGcIdS+0A2g28uHriMkZqqRCmzFczue5Tk99VNtzBdehyk7Ae+DZ4xw5yT6GqYai8NTqv6MRwCB78uh5Mw== - dependencies: - "@babel/runtime" "^7.16.0" - "@types/react" "^18.2.79" - "@types/react-dom" "^18.2.25" - "@wordpress/escape-html" "^2.58.0" - change-case "^4.1.2" - is-plain-object "^5.0.0" - react "^18.3.0" - react-dom "^18.3.0" - "@wordpress/escape-html@*": version "3.11.0" resolved "https://registry.yarnpkg.com/@wordpress/escape-html/-/escape-html-3.11.0.tgz#44850f394981a27c511b0eb5b28f8851b938a056" @@ -3268,13 +2877,6 @@ dependencies: "@babel/runtime" "7.25.7" -"@wordpress/escape-html@^2.15.0", "@wordpress/escape-html@^2.22.0", "@wordpress/escape-html@^2.58.0": - version "2.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/escape-html/-/escape-html-2.58.0.tgz#37fa8c74c31b7a481d56bab6a8f0bfa415311b2f" - integrity sha512-9YJXMNfzkrhHEVP1jFEhgijbZqW8Mt3NHIMZjIQoWtBf7QE86umpYpGGBXzYC0YlpGTRGzZTBwYaqFKxjeaSgA== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/eslint-plugin@*": version "21.4.0" resolved "https://registry.yarnpkg.com/@wordpress/eslint-plugin/-/eslint-plugin-21.4.0.tgz#9499d73c1930b0ba3c598b22f76ac3589ec683de" @@ -3305,13 +2907,6 @@ dependencies: "@babel/runtime" "7.25.7" -"@wordpress/hooks@^3.15.0", "@wordpress/hooks@^3.5.0", "@wordpress/hooks@^3.58.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/hooks/-/hooks-3.58.0.tgz#68094f7e7e3f8cbc3ab68a0fe9ac2a9b3cfe55d6" - integrity sha512-9LB0ZHnZRQlORttux9t/xbAskF+dk2ujqzPGsVzc92mSKpQP3K2a5Wy74fUnInguB1vLUNHT6nrNdkVom5qX1Q== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/i18n@*": version "5.11.0" resolved "https://registry.yarnpkg.com/@wordpress/i18n/-/i18n-5.11.0.tgz#95a8d645df23600d8f40e3d719c9c8b2db1e238f" @@ -3324,27 +2919,6 @@ sprintf-js "^1.1.1" tannin "^1.2.0" -"@wordpress/i18n@^4.15.0", "@wordpress/i18n@^4.22.0", "@wordpress/i18n@^4.58.0": - version "4.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/i18n/-/i18n-4.58.0.tgz#d4327fa4dee4f82be7753e900700670fea316d30" - integrity sha512-VfvS3BWv/RDjRKD6PscIcvYfWKnGJcI/DEqyDgUMhxCM6NRwoL478CsUKTiGJIymeyRodNRfprdcF086DpGKYw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/hooks" "^3.58.0" - gettext-parser "^1.3.1" - memize "^2.1.0" - sprintf-js "^1.1.1" - tannin "^1.2.0" - -"@wordpress/icons@^9.6.0": - version "9.49.0" - resolved "https://registry.yarnpkg.com/@wordpress/icons/-/icons-9.49.0.tgz#3886fcb99c01caae97f25bfa15a7fd6b0a818d88" - integrity sha512-Z8F+ledkfkcKDuS1c/RkM0dEWdfv2AXs6bCgey89p0atJSscf7qYbMJR9zE5rZ5aqXyFfV0DAFKJEgayNqneNQ== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/element" "^5.35.0" - "@wordpress/primitives" "^3.56.0" - "@wordpress/is-shallow-equal@*": version "5.11.0" resolved "https://registry.yarnpkg.com/@wordpress/is-shallow-equal/-/is-shallow-equal-5.11.0.tgz#2f273d6d4de24a66a7a8316b770cf832d22bfc37" @@ -3352,13 +2926,6 @@ dependencies: "@babel/runtime" "7.25.7" -"@wordpress/is-shallow-equal@^4.15.0", "@wordpress/is-shallow-equal@^4.22.0", "@wordpress/is-shallow-equal@^4.58.0": - version "4.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/is-shallow-equal/-/is-shallow-equal-4.58.0.tgz#52f400dc9fac721a0763b1c3f2932d383286b581" - integrity sha512-NH2lbXo/6ix1t4Zu9UBXpXNtoLwSaYmIRSyDH34XNb0ic8a7yjEOhYWVW3LTfSCv9dJVyxlM5TJPtL85q7LdeQ== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/jest-console@*": version "8.11.0" resolved "https://registry.yarnpkg.com/@wordpress/jest-console/-/jest-console-8.11.0.tgz#a825f4d9ee4eb007c9b6329687f8507dc30b49d2" @@ -3383,23 +2950,6 @@ "@babel/runtime" "7.25.7" "@wordpress/i18n" "*" -"@wordpress/keycodes@^3.15.0", "@wordpress/keycodes@^3.22.0", "@wordpress/keycodes@^3.58.0": - version "3.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/keycodes/-/keycodes-3.58.0.tgz#cc4d2a7c2eb47c2b4718dd6ec0e47c1a77d1f8ba" - integrity sha512-Q/LRKpx8ndzuHlkxSQ2BD+NTYYKQPIneNNMng8hTAfyU7RFwXpqj06HpeOFGh4XIdPKCs/8hmucoLJRmmLmZJA== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/i18n" "^4.58.0" - -"@wordpress/notices@^3.3.2": - version "3.31.0" - resolved "https://registry.yarnpkg.com/@wordpress/notices/-/notices-3.31.0.tgz#a278767eaa2e7b704fe1f4d68f95c7984779737c" - integrity sha512-9WyaFaSr/vQc1K7cZLyPw1xBKqWfjpAKMJzWMzHYjwk1ldibhBWVLukicuolD6Y+9l+97IZHCoESBQwUeFo79Q== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/a11y" "^3.31.0" - "@wordpress/data" "^9.1.0" - "@wordpress/npm-package-json-lint-config@*": version "5.11.0" resolved "https://registry.yarnpkg.com/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-5.11.0.tgz#41fe1589927d4342b2e79268200279da0609daa7" @@ -3418,15 +2968,6 @@ resolved "https://registry.yarnpkg.com/@wordpress/prettier-config/-/prettier-config-4.11.0.tgz#6b3f9aa7e2698c0d78e644037c6778b5c1da12ce" integrity sha512-Aoc8+xWOyiXekodjaEjS44z85XK877LzHZqsQuhC0kNgneDLrKkwI5qNgzwzAMbJ9jI58MPqVISCOX0bDLUPbw== -"@wordpress/primitives@^3.13.0", "@wordpress/primitives@^3.56.0": - version "3.56.0" - resolved "https://registry.yarnpkg.com/@wordpress/primitives/-/primitives-3.56.0.tgz#4513180bd783edeb09c4eb721fb1adff84c0e5bd" - integrity sha512-NXBq1ODjl6inMWx/l7KCbATcjdoeIOqYeL9i9alqdAfWeKx1EH9PIvKWylIkqZk7erXxCxldiRkuyjTtwjNBxw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/element" "^5.35.0" - clsx "^2.1.1" - "@wordpress/priority-queue@*": version "3.11.0" resolved "https://registry.yarnpkg.com/@wordpress/priority-queue/-/priority-queue-3.11.0.tgz#01e1570a7a29372bb1d07cd22fd9cbc5b5d03b09" @@ -3435,14 +2976,6 @@ "@babel/runtime" "7.25.7" requestidlecallback "^0.3.0" -"@wordpress/priority-queue@^2.22.0", "@wordpress/priority-queue@^2.58.0": - version "2.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/priority-queue/-/priority-queue-2.58.0.tgz#02564bbb0700d9fdd93039149e44e9992b16ebd3" - integrity sha512-W+qCS8HJWsXG8gE6yK/H/IObowcghPrQMM3cQHtfd/U05yFNU1Bd/fbj3AO1fVRztktS47lIpi9m3ll1evPEHA== - dependencies: - "@babel/runtime" "^7.16.0" - requestidlecallback "^0.3.0" - "@wordpress/private-apis@*": version "1.11.0" resolved "https://registry.yarnpkg.com/@wordpress/private-apis/-/private-apis-1.11.0.tgz#d2b2e61d0b5a45638d7fd5760ea2c3a644a72551" @@ -3450,13 +2983,6 @@ dependencies: "@babel/runtime" "7.25.7" -"@wordpress/private-apis@^0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@wordpress/private-apis/-/private-apis-0.40.0.tgz#0b2eb46599db5a669cf5d950a36745c7c17eb59b" - integrity sha512-ZX/9Y8eA3C3K6LOj32bHFj+9tNV819CBd8+chqMmmlvQRcTngiuXbMbnSdZnnAr1gLQgNpH9PJ60dIwJnGSEtQ== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/redux-routine@*": version "5.11.0" resolved "https://registry.yarnpkg.com/@wordpress/redux-routine/-/redux-routine-5.11.0.tgz#244e50a0007ac4cd8105b954bbf17bc418de714e" @@ -3467,33 +2993,6 @@ is-promise "^4.0.0" rungen "^0.3.2" -"@wordpress/redux-routine@^4.22.0", "@wordpress/redux-routine@^4.58.0": - version "4.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/redux-routine/-/redux-routine-4.58.0.tgz#b4d10267d196f9bb50059a322191c86684ef366d" - integrity sha512-r0mMWFeJr93yPy2uY/M5+gdUUYj0Zu8+21OFFb5hyQ0z7UHIa3IdgQxzCaTbV1LDA1ZYJrjHeCnA6s4gNHjA2Q== - dependencies: - "@babel/runtime" "^7.16.0" - is-plain-object "^5.0.0" - is-promise "^4.0.0" - rungen "^0.3.2" - -"@wordpress/rich-text@^5.13.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@wordpress/rich-text/-/rich-text-5.20.0.tgz#c1e367f3503b5e9d89e949afe60fa11cc4facc40" - integrity sha512-7W4PksJ6/SnQ+KuwvZ0dlKSwbaS6ejvWBm2N8R5S79AzbdmB69BpDCz0U/GUfGDXDhrU9dpzg5NIivoW2LC8Kg== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/a11y" "^3.22.0" - "@wordpress/compose" "^5.20.0" - "@wordpress/data" "^7.6.0" - "@wordpress/deprecated" "^3.22.0" - "@wordpress/element" "^4.20.0" - "@wordpress/escape-html" "^2.22.0" - "@wordpress/i18n" "^4.22.0" - "@wordpress/keycodes" "^3.22.0" - memize "^1.1.0" - rememo "^4.0.0" - "@wordpress/scripts@^30.3.0": version "30.4.0" resolved "https://registry.yarnpkg.com/@wordpress/scripts/-/scripts-30.4.0.tgz#c44d1877e0cd43b91583b95b7e6920615c6ade8f" @@ -3578,14 +3077,6 @@ "@babel/runtime" "7.25.7" "@wordpress/is-shallow-equal" "*" -"@wordpress/undo-manager@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@wordpress/undo-manager/-/undo-manager-0.18.0.tgz#f087eaf7c42b67f96af2d3bc90ccdf27c741c988" - integrity sha512-upbzPEToa095XG+2JXLHaolF1LfXEMFS0lNMYV37myoUS+eZ7/tl9Gx+yU2+OqWy57TMwx33NlWUX/n+ynzPRw== - dependencies: - "@babel/runtime" "^7.16.0" - "@wordpress/is-shallow-equal" "^4.58.0" - "@wordpress/url@*": version "4.11.0" resolved "https://registry.yarnpkg.com/@wordpress/url/-/url-4.11.0.tgz#d62bc612b45cf3776a00fabd6b4e0d0b264a4550" @@ -3594,24 +3085,11 @@ "@babel/runtime" "7.25.7" remove-accents "^0.5.0" -"@wordpress/url@^3.4.1", "@wordpress/url@^3.59.0": - version "3.59.0" - resolved "https://registry.yarnpkg.com/@wordpress/url/-/url-3.59.0.tgz#6453180452d2e00f3ba45177c4340cf0ca4ad90d" - integrity sha512-GxvoMjYCav0w4CiX0i0h3qflrE/9rhLIZg5aPCQjbrBdwTxYR3Exfw0IJYcmVaTKXQOUU8fOxlDxULsbLmKe9w== - dependencies: - "@babel/runtime" "^7.16.0" - remove-accents "^0.5.0" - "@wordpress/warning@*": version "3.11.0" resolved "https://registry.yarnpkg.com/@wordpress/warning/-/warning-3.11.0.tgz#36c5a1c024a96c712ce1e5746be08009d84213f6" integrity sha512-tXCsxlMAYXbRCgZmVHsBkoBGnrytZPGGezGXANRTsyJ00QoQJgxvnH6u22Rs/NOIVHQ5o65/9jKC3g0e6qn7PA== -"@wordpress/warning@^2.15.0": - version "2.58.0" - resolved "https://registry.yarnpkg.com/@wordpress/warning/-/warning-2.58.0.tgz#1f2f2cd10302daa4e6d391037a49f875e8d12fcc" - integrity sha512-9bZlORhyMY2nbWozeyC5kqJsFzEPP4DCLhGmjtbv+YWGHttUrxUZEfrKdqO+rUODA8rP5zeIly1nCQOUnkw4Lg== - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -3679,21 +3157,6 @@ agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: dependencies: debug "^4.3.4" -airbnb-prop-types@^2.14.0, airbnb-prop-types@^2.15.0, airbnb-prop-types@^2.16.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2" - integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg== - dependencies: - array.prototype.find "^2.1.1" - function.prototype.name "^1.1.2" - is-regex "^1.1.0" - object-is "^1.1.2" - object.assign "^4.1.0" - object.entries "^1.1.2" - prop-types "^15.7.2" - prop-types-exact "^1.2.0" - react-is "^16.13.1" - ajv-errors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -3859,17 +3322,6 @@ array-uniq@^1.0.1: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== -array.prototype.find@^2.1.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.3.tgz#675a233dbcd9b65ecf1fb3f915741aebc45461e6" - integrity sha512-fO/ORdOELvjbbeIfZfzrXFMhYHGofRGqd+am9zm3tZ4GlJINj/pA2eITyfd65Vg6+ZbHd/Cys7stpoRSWtQFdA== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - array.prototype.findlast@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" @@ -3894,7 +3346,7 @@ array.prototype.findlastindex@^1.2.5: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.flat@^1.2.1, array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== @@ -4212,11 +3664,6 @@ body-parser@1.20.3: type-is "~1.6.18" unpipe "1.0.0" -body-scroll-lock@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" - integrity sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg== - bonjour-service@^1.0.11: version "1.2.1" resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" @@ -4252,11 +3699,6 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -brcast@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brcast/-/brcast-2.0.2.tgz#2db16de44140e418dc37fab10beec0369e78dcef" - integrity sha512-Tfn5JSE7hrUlFcOoaLzVvkbgIemIorMIyoMr3TgvszWW7jFt2C9PdeMLtysYD9RU0MmU17b69+XJG1eRY2OBRg== - browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.23.0, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2: version "4.24.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" @@ -4483,7 +3925,7 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== -classnames@^2.3.1: +classnames@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== @@ -4496,7 +3938,7 @@ clean-webpack-plugin@^3.0.0: "@types/webpack" "^4.4.31" del "^4.1.1" -clipboard@^2.0.11, clipboard@^2.0.8: +clipboard@^2.0.11: version "2.0.11" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5" integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw== @@ -4534,11 +3976,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clsx@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" - integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -4561,7 +3998,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colord@^2.7.0, colord@^2.9.3: +colord@^2.9.3: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== @@ -4633,11 +4070,6 @@ compression@^1.7.4: safe-buffer "5.2.1" vary "~1.1.2" -compute-scroll-into-view@^1.0.17: - version "1.0.20" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43" - integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4660,11 +4092,6 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -"consolidated-events@^1.1.1 || ^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/consolidated-events/-/consolidated-events-2.0.2.tgz#da8d8f8c2b232831413d9e190dc11669c79f4a91" - integrity sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ== - constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -4735,7 +4162,7 @@ core-js-pure@^3.23.3: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.39.0.tgz#aa0d54d70a15bdc13e7c853db87c10abc30d68f3" integrity sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg== -core-js@^2.4.0, core-js@^2.6.5: +core-js@^2.4.0: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== @@ -5096,11 +4523,6 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - deepmerge@^4.2.2, deepmerge@^4.3.0, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -5127,7 +4549,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -5225,11 +4647,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -direction@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442" - integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ== - dns-packet@^5.2.2: version "5.6.1" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" @@ -5251,13 +4668,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -document.contains@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/document.contains/-/document.contains-1.0.2.tgz#4260abad67a6ae9e135c1be83d68da0db169d5f0" - integrity sha512-YcvYFs15mX8m3AO1QNQy3BlIpSMfNRj3Ujk2BEJxsZG+HZf7/hZ6jr7mDpXrF8q+ff95Vef5yjhiZxm8CGJr6Q== - dependencies: - define-properties "^1.1.3" - dom-helpers@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -5266,11 +4676,6 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dom-scroll-into-view@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz#e8f36732dd089b0201a88d7815dc3f88e6d66c7e" - integrity sha512-LwNVg3GJOprWDO+QhLL1Z9MMgWe/KAFLxVWKzjRTxNSPn8/LLDIfmuG71YHznXCqaqTjvHJDYO1MEAgX6XCNbQ== - dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -5323,17 +4728,6 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -downshift@^6.0.15: - version "6.1.12" - resolved "https://registry.yarnpkg.com/downshift/-/downshift-6.1.12.tgz#f14476b41a6f6fd080c340bad1ddf449f7143f6f" - integrity sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA== - dependencies: - "@babel/runtime" "^7.14.8" - compute-scroll-into-view "^1.0.17" - prop-types "^15.7.2" - react-is "^17.0.2" - tslib "^2.3.0" - duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5429,14 +4823,6 @@ envinfo@^7.7.3: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== -enzyme-shallow-equal@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz#4e3aa678022387a68e6c47aff200587851885b5e" - integrity sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg== - dependencies: - hasown "^2.0.0" - object-is "^1.1.5" - equivalent-key-map@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz#be4d57049bb8d46a81d6e256c1628465620c2a13" @@ -5553,7 +4939,7 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.1, es-set-tostringtag@^2.0.3: +es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== @@ -6227,27 +5613,6 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -framer-motion@^6.2.8: - version "6.5.1" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" - integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== - dependencies: - "@motionone/dom" "10.12.0" - framesync "6.0.1" - hey-listen "^1.0.8" - popmotion "11.0.3" - style-value-types "5.0.0" - tslib "^2.1.0" - optionalDependencies: - "@emotion/is-prop-valid" "^0.8.2" - -framesync@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20" - integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA== - dependencies: - tslib "^2.1.0" - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -6287,7 +5652,7 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.2, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -6408,14 +5773,6 @@ glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -global-cache@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/global-cache/-/global-cache-1.2.1.tgz#39ca020d3dd7b3f0934c52b75363f8d53312c16d" - integrity sha512-EOeUaup5DgWKlCMhA9YFqNRIlZwoxt731jCh47WBV9fQqHgXhr3Fa55hfgIUqilIcPsfdNKN7LHjrNY+Km40KA== - dependencies: - define-properties "^1.1.2" - is-symbol "^1.0.1" - global-modules@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" @@ -6529,11 +5886,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gradient-parser@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/gradient-parser/-/gradient-parser-0.1.5.tgz#0c7e2179559e5ce7d8d71f4423af937100b2248c" - integrity sha512-+uPlcVbjrKOnTzvz0MjTj7BfACj8OmxIa1moIjJV7btvhUMSJk0D47RfDCgDrZE3dYMz9Cf5xKJwnrKLjUq0KQ== - graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -6605,29 +5957,12 @@ header-case@^2.0.4: capital-case "^1.0.4" tslib "^2.0.3" -hey-listen@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" - integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== - hi-base32@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.1.tgz#1279f2ddae2673219ea5870c2121d2a33132857e" integrity sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA== -highlight-words-core@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" - integrity sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ== - -history@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" - integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== - dependencies: - "@babel/runtime" "^7.7.6" - -hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7147,7 +6482,7 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-regex@^1.1.0, is-regex@^1.1.4: +is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -7179,18 +6514,13 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.1, is-symbol@^1.0.2, is-symbol@^1.0.3: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" -is-touch-device@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-touch-device/-/is-touch-device-1.0.1.tgz#9a2fd59f689e9a9bf6ae9a86924c4ba805a42eab" - integrity sha512-LAYzo9kMT1b2p19L/1ATGt2XcSilnzNlyvq6c0pbPRVisLbAPpLqr53tIJS00kvrTkj0HtR8U7+u8X0yR8lPSw== - is-typed-array@^1.1.13: version "1.1.13" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" @@ -8089,7 +7419,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.1.1, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8275,11 +7605,6 @@ memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" -memize@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/memize/-/memize-1.1.0.tgz#4a5a684ac6992a13b1299043f3e49b1af6a0b0d3" - integrity sha512-K4FcPETOMTwe7KL2LK0orMhpOmWD2wRGwWWpbZy0fyArwsyIKR8YJVz8+efBAh3BO4zPqlSICu4vsLTRRqtFAg== - memize@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/memize/-/memize-2.1.0.tgz#6ddd4717887d94825748149ece00d04cf868ce0d" @@ -8463,18 +7788,6 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -moment-timezone@^0.5.40: - version "0.5.46" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.46.tgz#a21aa6392b3c6b3ed916cd5e95858a28d893704a" - integrity sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw== - dependencies: - moment "^2.29.4" - -moment@>=1.6.0, moment@^2.26.0, moment@^2.29.4: - version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - mousetrap@^1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9" @@ -8682,20 +7995,12 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== -object-is@^1.1.2, object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.5: +object.assign@^4.1.4, object.assign@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -8705,7 +8010,7 @@ object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4, object.assign@ has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.2, object.entries@^1.1.8: +object.entries@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== @@ -8733,7 +8038,7 @@ object.groupby@^1.0.3: define-properties "^1.2.1" es-abstract "^1.23.2" -object.values@^1.1.0, object.values@^1.1.5, object.values@^1.1.6, object.values@^1.2.0: +object.values@^1.1.6, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -8994,11 +8299,6 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -9057,16 +8357,6 @@ plur@^4.0.0: dependencies: irregular-plurals "^3.2.0" -popmotion@11.0.3: - version "11.0.3" - resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" - integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA== - dependencies: - framesync "6.0.1" - hey-listen "^1.0.8" - style-value-types "5.0.0" - tslib "^2.1.0" - possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -9405,19 +8695,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types-exact@^1.2.0: - version "1.2.5" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.5.tgz#f275e7dc0d629c2f7414782e8189b3e2d2e9e158" - integrity sha512-wHDhA5TSSvU07gdzsdeT/FZg6zay94K4Y7swSK4YsRG3moWB0Qsp9g1Y5BBausP1HF8K4UeVe2Xt7ZFJByKp6A== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - hasown "^2.0.2" - isarray "^2.0.5" - object.assign "^4.1.5" - reflect.ownkeys "^1.1.4" - -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -9526,7 +8804,7 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -qs@6.13.0, qs@^6.10.3: +qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== @@ -9553,13 +8831,6 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -raf@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -9582,46 +8853,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -re-resizable@^6.4.0: - version "6.10.1" - resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.10.1.tgz#d062ca50bbc4ec7ae940f756cba36479e9015bc5" - integrity sha512-m33nSWRH57UZLmep5M/LatkZ2NRqimVD/bOOpvymw5Zf33+eTSEixsUugscOZzAtK0/nx+OSuOf8VbKJx/4ptw== - -react-colorful@^5.3.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b" - integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw== - -react-dates@^21.8.0: - version "21.8.0" - resolved "https://registry.yarnpkg.com/react-dates/-/react-dates-21.8.0.tgz#355c3c7a243a7c29568fe00aca96231e171a5e94" - integrity sha512-PPriGqi30CtzZmoHiGdhlA++YPYPYGCZrhydYmXXQ6RAvAsaONcPtYgXRTLozIOrsQ5mSo40+DiA5eOFHnZ6xw== - dependencies: - airbnb-prop-types "^2.15.0" - consolidated-events "^1.1.1 || ^2.0.0" - enzyme-shallow-equal "^1.0.0" - is-touch-device "^1.0.1" - lodash "^4.1.1" - object.assign "^4.1.0" - object.values "^1.1.0" - prop-types "^15.7.2" - raf "^3.4.1" - react-moment-proptypes "^1.6.0" - react-outside-click-handler "^1.2.4" - react-portal "^4.2.0" - react-with-direction "^1.3.1" - react-with-styles "^4.1.0" - react-with-styles-interface-css "^6.0.0" - -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" - react-dom@^18.3.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -9635,41 +8866,11 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-moment-proptypes@^1.6.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/react-moment-proptypes/-/react-moment-proptypes-1.8.1.tgz#7ba4076147f6b5998f0d4f51d302d6d8c62049fd" - integrity sha512-Er940DxWoObfIqPrZNfwXKugjxMIuk1LAuEzn23gytzV6hKS/sw108wibi9QubfMN4h+nrlje8eUCSbQRJo2fQ== - dependencies: - moment ">=1.6.0" - -react-outside-click-handler@^1.2.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115" - integrity sha512-Te/7zFU0oHpAnctl//pP3hEAeobfeHMyygHB8MnjP6sX5OR8KHT1G3jmLsV3U9RnIYo+Yn+peJYWu+D5tUS8qQ== - dependencies: - airbnb-prop-types "^2.15.0" - consolidated-events "^1.1.1 || ^2.0.0" - document.contains "^1.0.1" - object.values "^1.1.0" - prop-types "^15.7.2" - -react-portal@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.2.tgz#bff1e024147d6041ba8c530ffc99d4c8248f49fa" - integrity sha512-vS18idTmevQxyQpnde0Td6ZcUlv+pD8GTyR42n3CHUQq9OHi1C4jDE4ZWEbEsrbrLRhSECYiao58cvocwMtP7Q== - dependencies: - prop-types "^15.5.8" - react-refresh@^0.14.0: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -9700,47 +8901,6 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react-with-direction@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-with-direction/-/react-with-direction-1.4.0.tgz#ebdf64d685d0650ce966e872e6431ad5a2485444" - integrity sha512-ybHNPiAmaJpoWwugwqry9Hd1Irl2hnNXlo/2SXQBwbLn/jGMauMS2y9jw+ydyX5V9ICryCqObNSthNt5R94xpg== - dependencies: - airbnb-prop-types "^2.16.0" - brcast "^2.0.2" - deepmerge "^1.5.2" - direction "^1.0.4" - hoist-non-react-statics "^3.3.2" - object.assign "^4.1.2" - object.values "^1.1.5" - prop-types "^15.7.2" - -react-with-styles-interface-css@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/react-with-styles-interface-css/-/react-with-styles-interface-css-6.0.0.tgz#b53da7fa8359d452cb934cface8738acaef7b5fe" - integrity sha512-6khSG1Trf4L/uXOge/ZAlBnq2O2PEXlQEqAhCRbvzaQU4sksIkdwpCPEl6d+DtP3+IdhyffTWuHDO9lhe1iYvA== - dependencies: - array.prototype.flat "^1.2.1" - global-cache "^1.2.1" - -react-with-styles@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/react-with-styles/-/react-with-styles-4.2.0.tgz#0b8a8e5d94d082518b9f564f6fcf6103e28096c5" - integrity sha512-tZCTY27KriRNhwHIbg1NkSdTTOSfXDg6Z7s+Q37mtz0Ym7Sc7IOr3PzVt4qJhJMW6Nkvfi3g34FuhtiGAJCBQA== - dependencies: - airbnb-prop-types "^2.14.0" - hoist-non-react-statics "^3.2.1" - object.assign "^4.1.0" - prop-types "^15.7.2" - react-with-direction "^1.3.1" - -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - react@^18.3.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -9808,36 +8968,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -reakit-system@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/reakit-system/-/reakit-system-0.15.2.tgz#a485fab84b3942acbed6212c3b56a6ef8611c457" - integrity sha512-TvRthEz0DmD0rcJkGamMYx+bATwnGNWJpe/lc8UV2Js8nnPvkaxrHk5fX9cVASFrWbaIyegZHCWUBfxr30bmmA== - dependencies: - reakit-utils "^0.15.2" - -reakit-utils@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/reakit-utils/-/reakit-utils-0.15.2.tgz#b4d5836e534576bfd175171541d43182ad97f2d2" - integrity sha512-i/RYkq+W6hvfFmXw5QW7zvfJJT/K8a4qZ0hjA79T61JAFPGt23DsfxwyBbyK91GZrJ9HMrXFVXWMovsKBc1qEQ== - -reakit-warning@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/reakit-warning/-/reakit-warning-0.6.2.tgz#9c346ae483eb1f284f2088653f90cabd26dbee56" - integrity sha512-z/3fvuc46DJyD3nJAUOto6inz2EbSQTjvI/KBQDqxwB0y02HDyeP8IWOJxvkuAUGkWpeSx+H3QWQFSNiPcHtmw== - dependencies: - reakit-utils "^0.15.2" - -reakit@^1.3.8: - version "1.3.11" - resolved "https://registry.yarnpkg.com/reakit/-/reakit-1.3.11.tgz#c15360ac43e94fbe4291d233af3ac5040428252e" - integrity sha512-mYxw2z0fsJNOQKAEn5FJCPTU3rcrY33YZ/HzoWqZX0G7FwySp1wkCYW79WhuYMNIUFQ8s3Baob1RtsEywmZSig== - dependencies: - "@popperjs/core" "^2.5.4" - body-scroll-lock "^3.1.5" - reakit-system "^0.15.2" - reakit-utils "^0.15.2" - reakit-warning "^0.6.2" - rechoir@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" @@ -9873,17 +9003,6 @@ reflect.getprototypeof@^1.0.4: globalthis "^1.0.3" which-builtin-type "^1.1.3" -reflect.ownkeys@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-1.1.4.tgz#3cf21da448f2aff8aba63ca601f65c99482e692c" - integrity sha512-iUNmtLgzudssL+qnTUosCmnq3eczlrVd1wXrgx/GhiI/8FvwrTYWtCJ9PNvWIRX+4ftupj2WUfB5mu5s9t6LnA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-set-tostringtag "^2.0.1" - globalthis "^1.0.3" - regenerate-unicode-properties@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" @@ -9901,11 +9020,6 @@ regenerator-runtime@^0.10.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" integrity sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w== -regenerator-runtime@^0.13.2: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - regenerator-runtime@^0.14.0: version "0.14.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" @@ -9952,16 +9066,11 @@ regjsparser@^0.11.0: dependencies: jsesc "~3.0.2" -rememo@^4.0.0, rememo@^4.0.2: +rememo@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/rememo/-/rememo-4.0.2.tgz#8af1f09fd3bf5809ca0bfd0b803926c67ead8c1e" integrity sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ== -remove-accents@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.4.tgz#73704abf7dae3764295d475d2b6afac4ea23e4d9" - integrity sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg== - remove-accents@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" @@ -10183,14 +9292,6 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -10780,14 +9881,6 @@ style-search@^0.1.0: resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== -style-value-types@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad" - integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA== - dependencies: - hey-listen "^1.0.8" - tslib "^2.1.0" - stylehacks@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.1.1.tgz#543f91c10d17d00a440430362d419f79c25545a6" @@ -11129,7 +10222,7 @@ tslib@^1.8.1, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: +tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.6.2: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -11141,11 +10234,6 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -turbo-combine-reducers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/turbo-combine-reducers/-/turbo-combine-reducers-1.0.2.tgz#aa3650b3c63daa6804d35a4042014f6d31df1e47" - integrity sha512-gHbdMZlA6Ym6Ur5pSH/UWrNQMIM9IqTH6SoL1DbHpqEdQ8i+cFunSmSlFykPt0eGQwZ4d/XTHOl74H0/kFBVWw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11376,7 +10464,7 @@ urlpattern-polyfill@10.0.0: resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz#f0a03a97bfb03cdf33553e5e79a2aadd22cac8ec" integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg== -use-isomorphic-layout-effect@^1.1.1, use-isomorphic-layout-effect@^1.1.2: +use-isomorphic-layout-effect@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== @@ -11396,7 +10484,7 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== From 0b48d9c2272c9093cfa9c07c2e0a716aa30fda91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Fri, 13 Dec 2024 13:04:41 +0100 Subject: [PATCH 127/169] avoid adding JS when fastlane is disabled --- modules/ppcp-axo/src/AxoModule.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 8be0f3c22..21ee84a35 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -247,7 +247,10 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { add_filter( 'woocommerce_paypal_payments_sdk_components_hook', function( $components ) use ( $c ) { - if ( ! $c->has( 'axo.available' ) || ! $c->get( 'axo.available' ) ) { + $dcc_configuration = $c->get( 'wcgateway.configuration.dcc' ); + assert( $dcc_configuration instanceof DCCGatewayConfiguration ); + + if ( ! $dcc_configuration->use_fastlane() ) { return $components; } $components[] = 'fastlane'; @@ -258,14 +261,18 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { add_action( 'wp_head', function () use ( $c ) { - // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript - echo ''; - // Add meta tag to allow feature-detection of the site's AXO payment state. $dcc_configuration = $c->get( 'wcgateway.configuration.dcc' ); assert( $dcc_configuration instanceof DCCGatewayConfiguration ); - $this->add_feature_detection_tag( $dcc_configuration->use_fastlane() ); + if ( $dcc_configuration->use_fastlane() ) { + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + echo ''; + + $this->add_feature_detection_tag( true ); + } else { + $this->add_feature_detection_tag( false ); + } } ); From 83b637cf5e7cd536c3a0ea39585557e6513fea25 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 13 Dec 2024 16:12:27 +0400 Subject: [PATCH 128/169] If free trial is in the product the fundingSource should be only "paypal" --- modules/ppcp-blocks/resources/js/checkout-block.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index 03c60745a..3ddc2cd92 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -132,10 +132,11 @@ if ( blockEnabled ) { }, } ); } else if ( config.smartButtonsEnabled ) { - for ( const fundingSource of [ - 'paypal', - ...config.enabledFundingSources, - ] ) { + const fundingSources = config.scriptData.is_free_trial_cart + ? [ 'paypal' ] + : [ 'paypal', ...config.enabledFundingSources ]; + + for ( const fundingSource of fundingSources ) { registerExpressPaymentMethod( { name: `${ config.id }-${ fundingSource }`, title: 'PayPal', From dec6e040e678397b4b08111376e4eeb5cdcc6941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Mon, 16 Dec 2024 14:05:35 +0100 Subject: [PATCH 129/169] Update required WP Version to 6.5 and WC version to 9.2 --- readme.txt | 2 +- src/FilePathPluginFactory.php | 2 +- woocommerce-paypal-payments.php | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index e11d40263..93baa02a9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ === WooCommerce PayPal Payments === Contributors: woocommerce, automattic, syde Tags: woocommerce, paypal, payments, ecommerce, credit card -Requires at least: 6.3 +Requires at least: 6.5 Tested up to: 6.7 Requires PHP: 7.4 Stable tag: 2.9.5 diff --git a/src/FilePathPluginFactory.php b/src/FilePathPluginFactory.php index e62fe1add..34f028ffa 100644 --- a/src/FilePathPluginFactory.php +++ b/src/FilePathPluginFactory.php @@ -85,7 +85,7 @@ class FilePathPluginFactory implements FilePathPluginFactoryInterface { 'Title' => '', 'Description' => '', 'TextDomain' => '', - 'RequiresWP' => '6.3', + 'RequiresWP' => '6.5', 'RequiresPHP' => '7.4', ), $plugin_data diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index 1544895ca..36beac2f3 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -9,7 +9,8 @@ * License: GPL-2.0 * Requires PHP: 7.4 * Requires Plugins: woocommerce - * WC requires at least: 6.9 + * Requires at least: 6.5 + * WC requires at least: 9.2 * WC tested up to: 9.4 * Text Domain: woocommerce-paypal-payments * From 51613c30201aaaafe951e5356e35346dd5ab008c Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 16 Dec 2024 16:00:27 +0100 Subject: [PATCH 130/169] Fix toggle animation issue --- .../SettingsBlocks/PaymentMethodItemBlock.js | 75 ++++++++----------- .../SettingsBlocks/PaymentMethodsBlock.js | 13 ++++ .../SettingsBlocks/SettingsBlock.js | 9 ++- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js index 3f3827f76..9dadf3023 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -5,56 +5,43 @@ import PaymentMethodIcon from '../PaymentMethodIcon'; import data from '../../../utils/data'; const PaymentMethodItemBlock = ( props ) => { - const [ paymentMethodState, setPaymentMethodState ] = useState(); + const [ isChecked, setIsChecked ] = useState( false ); const [ modalIsVisible, setModalIsVisible ] = useState( false ); const Modal = props?.modal; - const handleCheckboxState = ( checked ) => { - setPaymentMethodState( checked ? props.id : null ); - }; - return ( <> - ( -
-
- - - { props.title } - + +
+
+ + + { props.title } + +
+

+ { props.description } +

+
+ + { Modal && ( +
setModalIsVisible( true ) } + > + { data().getImage( 'icon-settings.svg' ) }
-

- { props.description } -

-
- - { Modal && ( -
- setModalIsVisible( true ) - } - > - { data().getImage( - 'icon-settings.svg' - ) } -
- ) } -
-
- ), - ] } - /> + ) } +
+
+ { Modal && modalIsVisible && ( ) } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js index 68a18ad4b..28ed05f8c 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js @@ -1,7 +1,14 @@ +import { useState, useCallback } from '@wordpress/element'; import SettingsBlock from './SettingsBlock'; import PaymentMethodItemBlock from './PaymentMethodItemBlock'; const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => { + const [ selectedMethod, setSelectedMethod ] = useState( null ); + + const handleSelect = useCallback( ( methodId, isSelected ) => { + setSelectedMethod( isSelected ? methodId : null ); + }, [] ); + if ( paymentMethods.length === 0 ) { return null; } @@ -16,6 +23,12 @@ const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => { + handleSelect( paymentMethod.id, checked ) + } /> ) ) } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js index 5e4985104..bb5de947a 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js @@ -1,13 +1,14 @@ -const SettingsBlock = ( { className, components = [] } ) => { +const SettingsBlock = ( { className, components = [], children } ) => { const blockClassName = [ 'ppcp-r-settings-block', className ].filter( Boolean ); return (
- { components.map( ( Component, index ) => ( - - ) ) } + { children || + components.map( ( Component, index ) => ( + + ) ) }
); }; From f32f30ef0aa67e2f4616bd3ac77a4eaaa3a10a5b Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 17 Dec 2024 11:50:32 +0100 Subject: [PATCH 131/169] Remove the components prop in favor of the native children prop to prevent unnecessary re-renders which are breaking component animations --- .../ReusableComponents/AccordionSection.js | 13 +- .../SettingsBlocks/AccordionSettingsBlock.js | 32 ++--- .../SettingsBlocks/ButtonSettingsBlock.js | 44 +++---- .../SettingsBlocks/FeatureSettingsBlock.js | 78 +++++------ .../SettingsBlocks/InputSettingsBlock.js | 36 ++--- .../SettingsBlocks/PaymentMethodsBlock.js | 31 ++--- .../SettingsBlocks/RadioSettingsBlock.js | 57 ++++---- .../SettingsBlocks/SelectSettingsBlock.js | 34 ++--- .../SettingsBlocks/SettingsBlock.js | 11 +- .../SettingsBlocks/ToggleSettingsBlock.js | 48 +++---- .../TabSettingsElements/Blocks/OrderIntent.js | 81 +++++------- .../Blocks/SavePaymentMethods.js | 123 ++++++++---------- .../Blocks/Troubleshooting.js | 54 ++++---- 13 files changed, 271 insertions(+), 371 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js index f5b071945..aff2d9997 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/AccordionSection.js @@ -1,8 +1,6 @@ import { Icon } from '@wordpress/components'; import { chevronDown, chevronUp } from '@wordpress/icons'; - import classNames from 'classnames'; - import { useAccordionState } from '../../hooks/useAccordionState'; // Provide defaults for all layout components so the generic version just works. @@ -24,6 +22,13 @@ const DefaultDescription = ( { children } ) => (
{ children }
); +const AccordionContent = ( { isOpen, children } ) => { + if ( ! isOpen || ! children ) { + return null; + } + return
{ children }
; +}; + const Accordion = ( { title, id = '', @@ -65,9 +70,7 @@ const Accordion = ( { ) } - { isOpen && children && ( -
{ children }
- ) } + { children }
); }; 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 8a953c805..cf7f2cee4 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/AccordionSettingsBlock.js @@ -9,25 +9,19 @@ import { } from './SettingsBlockElements'; const SettingsAccordion = ( { title, description, children, ...props } ) => ( - ( - - { children } - - ), - ] } - /> + + + { children } + + ); export default SettingsAccordion; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js index e2b2aeb53..e66223ba5 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js @@ -3,32 +3,24 @@ import SettingsBlock from './SettingsBlock'; import { Header, Title, Action, Description } from './SettingsBlockElements'; const ButtonSettingsBlock = ( { title, description, ...props } ) => ( - ( - <> -
- { title } - { description } -
- - - - - ), - ] } - /> + +
+ { title } + { description } +
+ + + +
); export default ButtonSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js index 7b6010de6..2984e8074 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js @@ -11,56 +11,42 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => { } return ( - <> - - { notes.map( ( note, index ) => ( - { note } - ) ) } - - + + { notes.map( ( note, index ) => ( + { note } + ) ) } + ); }; return ( - ( - <> -
- - { title } - { props.actionProps?.featureStatus && ( - <TitleBadge - { ...props.actionProps?.badge } - /> - ) } - - - { description } - { printNotes() } - -
- -
- { props.actionProps?.buttons.map( - ( button ) => ( - - ) - ) } -
-
- - ), - ] } - /> + +
+ + { title } + { props.actionProps?.featureStatus && ( + <TitleBadge { ...props.actionProps?.badge } /> + ) } + + + { description } + { printNotes() } + +
+ +
+ { props.actionProps?.buttons.map( ( button ) => ( + + ) ) } +
+
+
); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/InputSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/InputSettingsBlock.js index 6f8a06e3b..3470e8b60 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/InputSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/InputSettingsBlock.js @@ -42,28 +42,20 @@ const InputSettingsBlock = ( { order = DEFAULT_ELEMENT_ORDER, ...props } ) => ( - ( - <> - { order.map( ( elementKey ) => { - const RenderElement = ELEMENT_RENDERERS[ elementKey ]; - return RenderElement ? ( - - ) : null; - } ) } - - ), - ] } - /> + + { order.map( ( elementKey ) => { + const RenderElement = ELEMENT_RENDERERS[ elementKey ]; + return RenderElement ? ( + + ) : null; + } ) } + ); export default InputSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js index 28ed05f8c..3ffb91051 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js @@ -16,25 +16,18 @@ const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => { return ( ( - <> - { paymentMethods.map( ( paymentMethod ) => ( - - handleSelect( paymentMethod.id, checked ) - } - /> - ) ) } - - ), - ] } - /> + > + { paymentMethods.map( ( paymentMethod ) => ( + + handleSelect( paymentMethod.id, checked ) + } + /> + ) ) } + ); }; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/RadioSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/RadioSettingsBlock.js index 9519f127f..44270d874 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/RadioSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/RadioSettingsBlock.js @@ -11,37 +11,32 @@ const RadioSettingsBlock = ( { ( - <> -
- { title } - { description } -
- { options.map( ( option ) => ( - - props.actionProps?.callback( - props.actionProps?.key, - newValue - ) - } - label={ option.label } - description={ option.description } - toggleAdditionalContent={ true } - > - { option.additionalContent } - - ) ) } - - ), - ] } - /> + > +
+ { title } + { description } +
+ { options.map( ( option ) => ( + + props.actionProps?.callback( + props.actionProps?.key, + newValue + ) + } + label={ option.label } + description={ option.description } + toggleAdditionalContent={ true } + > + { option.additionalContent } + + ) ) } +
); export default RadioSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SelectSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SelectSettingsBlock.js index acef1d29b..5436d0921 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SelectSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SelectSettingsBlock.js @@ -35,27 +35,19 @@ const SelectSettingsBlock = ( { order = DEFAULT_ELEMENT_ORDER, ...props } ) => ( - ( - <> - { order.map( ( elementKey ) => { - const RenderElement = ELEMENT_RENDERERS[ elementKey ]; - return RenderElement ? ( - - ) : null; - } ) } - - ), - ] } - /> + + { 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 index bb5de947a..768fa9387 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/SettingsBlock.js @@ -1,16 +1,9 @@ -const SettingsBlock = ( { className, components = [], children } ) => { +const SettingsBlock = ( { className, children } ) => { const blockClassName = [ 'ppcp-r-settings-block', className ].filter( Boolean ); - return ( -
- { children || - components.map( ( Component, index ) => ( - - ) ) } -
- ); + return
{ children }
; }; export default SettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js index 3e0c0eac6..b66536b00 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ToggleSettingsBlock.js @@ -3,35 +3,25 @@ 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 } - ) } -
- ), - ] } - /> + + + + props.actionProps?.callback( + props.actionProps?.key, + newValue + ) + } + /> + +
+ { title && { title } } + { description && { description } } +
+
); export default ToggleSettingsBlock; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OrderIntent.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OrderIntent.js index 1d1b632fe..a53090ea1 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OrderIntent.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/OrderIntent.js @@ -9,55 +9,40 @@ import { const OrderIntent = ( { updateFormValue, settings } ) => { return ( - ( - <> -
- - { __( - 'Order Intent', - 'woocommerce-paypal-payments' - ) } - - - { __( - 'Choose between immediate capture or authorization-only, with manual capture in the Order section.', - 'woocommerce-paypal-payments' - ) } - -
- - ), - () => ( - <> - + +
+ + { __( '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/SavePaymentMethods.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/SavePaymentMethods.js index f10907c4c..aefa9f44c 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,82 +1,73 @@ import { __, sprintf } from '@wordpress/i18n'; import { + Header, SettingsBlock, ToggleSettingsBlock, Title, Description, } from '../../../../ReusableComponents/SettingsBlocks'; -import { Header } from '../../../../ReusableComponents/SettingsBlocks/SettingsBlockElements'; const SavePaymentMethods = ( { updateFormValue, settings } ) => { return ( - ( - <> -
- - { __( - 'Save payment methods', + <SettingsBlock className="ppcp-r-settings-block--save-payment-methods"> + <Header> + <Title> + { __( + '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' + ) } + +
+ + This will disable all Pay Later features and Alternative Payment Methods on your site.', 'woocommerce-paypal-payments' - ) } - - - { __( - 'Securely store customers’ payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.', - 'woocommerce-paypal-payments' - ) } - - - - ), - () => ( - 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', + ), + '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', + } } + /> + + +
); }; 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 f53a360c7..82cd635dc 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 @@ -36,36 +36,30 @@ const Troubleshooting = ( { updateFormValue, settings } ) => { value: settings.logging, } } /> - ( - <> -
- - { __( - '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' - ) } - - . - -
- - - ), - ] } - /> + +
+ + { __( + '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' + ) } + + . + +
+ +
Date: Tue, 17 Dec 2024 11:56:38 +0100 Subject: [PATCH 132/169] Make the toggle state name a bit more descriptive --- .../SettingsBlocks/PaymentMethodItemBlock.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js index 9dadf3023..cdad46b3e 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -5,7 +5,7 @@ import PaymentMethodIcon from '../PaymentMethodIcon'; import data from '../../../utils/data'; const PaymentMethodItemBlock = ( props ) => { - const [ isChecked, setIsChecked ] = useState( false ); + const [ toggleIsChecked, setToggleIsChecked ] = useState( false ); const [ modalIsVisible, setModalIsVisible ] = useState( false ); const Modal = props?.modal; @@ -28,8 +28,8 @@ const PaymentMethodItemBlock = ( props ) => {
{ Modal && (
Date: Mon, 16 Dec 2024 13:12:38 +0100 Subject: [PATCH 133/169] New Settings UI: Implement logic for features and refresh button. --- .../ppcp-googlepay/src/GooglepayModule.php | 20 +++ .../SettingsBlocks/FeatureSettingsBlock.js | 78 ++++++----- .../Screens/Overview/TabOverview.js | 84 +++++++++-- .../resources/js/data/common/action-types.js | 1 + .../resources/js/data/common/actions.js | 11 ++ .../resources/js/data/common/constants.js | 11 ++ .../resources/js/data/common/controls.js | 24 ++++ modules/ppcp-settings/services.php | 8 ++ .../src/Endpoint/CommonRestEndpoint.php | 5 + .../Endpoint/RefreshFeatureStatusEndpoint.php | 132 ++++++++++++++++++ modules/ppcp-settings/src/SettingsModule.php | 1 + 11 files changed, 328 insertions(+), 47 deletions(-) create mode 100644 modules/ppcp-settings/src/Endpoint/RefreshFeatureStatusEndpoint.php diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index a13ebf498..01d5f8fae 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -232,6 +232,26 @@ class GooglepayModule implements ServiceModule, ExtendingModule, ExecutableModul 2 ); + add_filter( + 'woocommerce_paypal_payments_rest_common_merchant_data', + function ( array $merchant_data ) use ( $c ): array { + if ( ! isset( $merchant_data['features'] ) ) { + $merchant_data['features'] = array(); + } + + $product_status = $c->get( 'googlepay.helpers.apm-product-status' ); + assert( $product_status instanceof ApmProductStatus ); + + $google_pay_enabled = $product_status->is_active(); + + $merchant_data['features']['google_pay'] = array( + 'enabled' => $google_pay_enabled, + ); + + return $merchant_data; + } + ); + return true; } } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js index 2984e8074..f2d66e9a3 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js @@ -11,42 +11,56 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => { } return ( - - { notes.map( ( note, index ) => ( - { note } - ) ) } - + <> + + { notes.map( ( note, index ) => ( + { note } + ) ) } + + ); }; return ( - -
- - { title } - { props.actionProps?.featureStatus && ( - <TitleBadge { ...props.actionProps?.badge } /> - ) } - - - { description } - { printNotes() } - -
- -
- { props.actionProps?.buttons.map( ( button ) => ( - - ) ) } -
-
-
+ ( + <> +
+ + { title } + { props.actionProps?.enabled && ( + <TitleBadge + { ...props.actionProps?.badge } + /> + ) } + + + { description } + { printNotes() } + +
+ +
+ { props.actionProps?.buttons.map( + ( button ) => ( + + ) + ) } +
+
+ + ), + ] } + /> ); }; 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 fe3e64218..fb7fe30c6 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js @@ -6,10 +6,42 @@ import TodoSettingsBlock from '../../ReusableComponents/SettingsBlocks/TodoSetti import FeatureSettingsBlock from '../../ReusableComponents/SettingsBlocks/FeatureSettingsBlock'; import { TITLE_BADGE_POSITIVE } from '../../ReusableComponents/TitleBadge'; import data from '../../../utils/data'; +import { useMerchantInfo } from '../../../data/common/hooks'; +import { useDispatch } from '@wordpress/data'; +import { STORE_NAME } from '../../../data/common'; const TabOverview = () => { const [ todos, setTodos ] = useState( [] ); const [ todosData, setTodosData ] = useState( todosDataDefault ); + const [ isRefreshing, setIsRefreshing ] = useState( false ); + + const { merchant } = useMerchantInfo(); + const { refreshFeatureStatuses } = useDispatch( STORE_NAME ); + + const features = featuresDefault.map( ( feature ) => { + const merchantFeature = merchant?.features?.[ feature.id ]; + return { + ...feature, + enabled: merchantFeature?.enabled ?? false, + }; + } ); + + const refreshHandler = async () => { + setIsRefreshing( true ); + + const result = await refreshFeatureStatuses(); + + if ( result && ! result.success ) { + console.error( + 'Failed to refresh features:', + result.message || 'Unknown error' + ); + } else { + console.log( 'Features refreshed successfully.' ); + } + + setIsRefreshing( false ); + }; return (
@@ -39,30 +71,54 @@ const TabOverview = () => { title={ __( 'Features', 'woocommerce-paypal-payments' ) } description={
-

{ __( 'Enable additional features…' ) }

-

{ __( 'Click Refresh…' ) }

-
} - contentItems={ featuresDefault.map( ( feature ) => ( + contentItems={ features.map( ( feature ) => ( ) ) } @@ -133,7 +189,6 @@ const featuresDefault = [ 'Advanced Credit and Debit Cards', 'woocommerce-paypal-payments' ), - featureStatus: true, description: __( 'Process major credit and debit cards including Visa, Mastercard, American Express and Discover.', 'woocommerce-paypal-payments' @@ -181,7 +236,6 @@ const featuresDefault = [ 'Let customers pay using their Google Pay wallet.', 'woocommerce-paypal-payments' ), - featureStatus: true, buttons: [ { type: 'secondary', 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 34e831508..ac08cdcf7 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -23,4 +23,5 @@ export default { DO_SANDBOX_LOGIN: 'COMMON:DO_SANDBOX_LOGIN', DO_PRODUCTION_LOGIN: 'COMMON:DO_PRODUCTION_LOGIN', DO_REFRESH_MERCHANT: 'COMMON:DO_REFRESH_MERCHANT', + DO_REFRESH_FEATURES: 'DO_REFRESH_FEATURES', }; diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 7dd13206e..be52ff1d8 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -189,3 +189,14 @@ export const connectViaIdAndSecret = function* () { export const refreshMerchantData = function* () { return yield { type: ACTION_TYPES.DO_REFRESH_MERCHANT }; }; + +/** + * Side effect. + * Purges all features status data via a REST request. + * Refreshes the merchant data via a REST request. + * + * @return {Action} The action. + */ +export const refreshFeatureStatuses = function* () { + return yield { type: ACTION_TYPES.DO_REFRESH_FEATURES }; +}; diff --git a/modules/ppcp-settings/resources/js/data/common/constants.js b/modules/ppcp-settings/resources/js/data/common/constants.js index 9499ef069..c67b1fef0 100644 --- a/modules/ppcp-settings/resources/js/data/common/constants.js +++ b/modules/ppcp-settings/resources/js/data/common/constants.js @@ -53,3 +53,14 @@ export const REST_MANUAL_CONNECTION_PATH = '/wc/v3/wc_paypal/connect_manual'; * @type {string} */ export const REST_CONNECTION_URL_PATH = '/wc/v3/wc_paypal/login_link'; + +/** + * REST path to refresh the feature status. + * + * Used by: Controls + * See: RefreshFeatureStatusEndpoint.php + * + * @type {string} + */ +export const REST_REFRESH_FEATURES_PATH = + '/wc/v3/wc_paypal/refresh-feature-status'; diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js index 7845f335f..e095d6acb 100644 --- a/modules/ppcp-settings/resources/js/data/common/controls.js +++ b/modules/ppcp-settings/resources/js/data/common/controls.js @@ -16,6 +16,7 @@ import { REST_MANUAL_CONNECTION_PATH, REST_CONNECTION_URL_PATH, REST_HYDRATE_MERCHANT_PATH, + REST_REFRESH_FEATURES_PATH, } from './constants'; import ACTION_TYPES from './action-types'; @@ -121,4 +122,27 @@ export const controls = { return result; }, + + async [ ACTION_TYPES.DO_REFRESH_FEATURES ]() { + let result = null; + + try { + result = await apiFetch( { + path: REST_REFRESH_FEATURES_PATH, + method: 'POST', + } ); + + if ( result.success ) { + result = await dispatch( STORE_NAME ).refreshMerchantData(); + } + } catch ( e ) { + result = { + success: false, + error: e, + message: e.message, + }; + } + + return result; + }, }; diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index c1eeca241..349c13350 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\ConnectManualRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint; +use WooCommerce\PayPalCommerce\Settings\Endpoint\RefreshFeatureStatusEndpoint; use WooCommerce\PayPalCommerce\Settings\Endpoint\SwitchSettingsUiEndpoint; use WooCommerce\PayPalCommerce\Settings\Service\ConnectionUrlGenerator; use WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager; @@ -70,6 +71,13 @@ return array( 'settings.rest.common' => static function ( ContainerInterface $container ) : CommonRestEndpoint { return new CommonRestEndpoint( $container->get( 'settings.data.common' ) ); }, + 'settings.rest.refresh_feature_status' => static function ( ContainerInterface $container ) : RefreshFeatureStatusEndpoint { + return new RefreshFeatureStatusEndpoint( + $container->get( 'wcgateway.settings' ), + new Cache( 'ppcp-timeout' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, 'settings.rest.connect_manual' => static function ( ContainerInterface $container ) : ConnectManualRestEndpoint { return new ConnectManualRestEndpoint( $container->get( 'api.paypal-host-production' ), diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 3c0131759..7524e7e31 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -206,6 +206,11 @@ class CommonRestEndpoint extends RestEndpoint { $this->merchant_info_map ); + $extra_data['merchant'] = apply_filters( + 'woocommerce_paypal_payments_rest_common_merchant_data', + $extra_data['merchant'], + ); + return $extra_data; } diff --git a/modules/ppcp-settings/src/Endpoint/RefreshFeatureStatusEndpoint.php b/modules/ppcp-settings/src/Endpoint/RefreshFeatureStatusEndpoint.php new file mode 100644 index 000000000..d8fc2760e --- /dev/null +++ b/modules/ppcp-settings/src/Endpoint/RefreshFeatureStatusEndpoint.php @@ -0,0 +1,132 @@ +settings = $settings; + $this->cache = $cache; + $this->logger = $logger; + } + + /** + * Configure REST API routes. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'refresh_status' ), + 'permission_callback' => array( $this, 'check_permission' ), + ), + ) + ); + } + + /** + * Handles the refresh status request. + * + * @param WP_REST_Request $request Full data about the request. + * @return WP_REST_Response + */ + public function refresh_status( WP_REST_Request $request ): WP_REST_Response { + $now = time(); + $last_request_time = $this->cache->get( self::CACHE_KEY ) ?: 0; + $seconds_missing = $last_request_time + self::TIMEOUT - $now; + + if ( $seconds_missing > 0 ) { + return $this->return_error( + sprintf( + // translators: %1$s is the number of seconds remaining. + __( 'Wait %1$s seconds before trying again.', 'woocommerce-paypal-payments' ), + $seconds_missing + ) + ); + } + + $this->cache->set( self::CACHE_KEY, $now, self::TIMEOUT ); + + do_action( 'woocommerce_paypal_payments_clear_apm_product_status', $this->settings ); + + $this->logger->info( 'Feature status refreshed successfully' ); + + return $this->return_success( + array( + 'message' => __( 'Feature status refreshed successfully.', 'woocommerce-paypal-payments' ), + ) + ); + } +} diff --git a/modules/ppcp-settings/src/SettingsModule.php b/modules/ppcp-settings/src/SettingsModule.php index 7cb55bb02..f2d7267e0 100644 --- a/modules/ppcp-settings/src/SettingsModule.php +++ b/modules/ppcp-settings/src/SettingsModule.php @@ -181,6 +181,7 @@ class SettingsModule implements ServiceModule, ExecutableModule { $container->get( 'settings.rest.common' ), $container->get( 'settings.rest.connect_manual' ), $container->get( 'settings.rest.login_link' ), + $container->get( 'settings.rest.refresh_feature_status' ), ); foreach ( $endpoints as $endpoint ) { From fa2571f44bcf9791b83725df95bae87230862741 Mon Sep 17 00:00:00 2001 From: Himad M Date: Mon, 16 Dec 2024 15:43:22 +0100 Subject: [PATCH 134/169] New Settings UI: Add modules feature state to merchant data --- modules/ppcp-applepay/src/ApplepayModule.php | 20 +++++++++++++ .../ppcp-wc-gateway/src/WCGatewayModule.php | 28 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index c5cc6a1b9..aa9876069 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -182,6 +182,26 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule 2 ); + add_filter( + 'woocommerce_paypal_payments_rest_common_merchant_data', + function( array $merchant_data ) use ( $c ): array { + if ( ! isset( $merchant_data['features'] ) ) { + $merchant_data['features'] = array(); + } + + $product_status = $c->get( 'applepay.apple-product-status' ); + assert( $product_status instanceof AppleProductStatus ); + + $apple_pay_enabled = $product_status->is_active(); + + $merchant_data['features']['apple_pay'] = array( + 'enabled' => $apple_pay_enabled, + ); + + return $merchant_data; + } + ); + return true; } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 8c807474e..f754a3cbe 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -13,6 +13,7 @@ use Exception; use Psr\Log\LoggerInterface; use Throwable; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\Orders; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; @@ -547,6 +548,33 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul } ); + add_filter( + 'woocommerce_paypal_payments_rest_common_merchant_data', + function( array $merchant_data ) use ( $c ): array { + if ( ! isset( $merchant_data['features'] ) ) { + $merchant_data['features'] = array(); + } + + $billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' ); + assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint ); + + $reference_transactions_enabled = $billing_agreements_endpoint->reference_transaction_enabled(); + $merchant_data['features']['save_paypal_and_venmo'] = array( + 'enabled' => $reference_transactions_enabled, + ); + + $dcc_product_status = $c->get( 'wcgateway.helper.dcc-product-status' ); + assert( $dcc_product_status instanceof DCCProductStatus ); + + $dcc_enabled = $dcc_product_status->dcc_is_active(); + $merchant_data['features']['advanced_credit_and_debit_cards'] = array( + 'enabled' => $dcc_enabled, + ); + + return $merchant_data; + } + ); + return true; } From f31b78f91c0f9a459d57b1bcb32989bb353334f5 Mon Sep 17 00:00:00 2001 From: Himad M Date: Tue, 17 Dec 2024 12:45:54 +0100 Subject: [PATCH 135/169] New Settings UI: Move request chaining fom control to action --- .../resources/js/data/common/actions.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index be52ff1d8..1982d7f42 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -7,7 +7,7 @@ * @file */ -import { select } from '@wordpress/data'; +import { dispatch, select } from '@wordpress/data'; import ACTION_TYPES from './action-types'; import { STORE_NAME } from './constants'; @@ -192,11 +192,17 @@ export const refreshMerchantData = function* () { /** * Side effect. - * Purges all features status data via a REST request. + * Purges all feature status data via a REST request. * Refreshes the merchant data via a REST request. * * @return {Action} The action. */ export const refreshFeatureStatuses = function* () { - return yield { type: ACTION_TYPES.DO_REFRESH_FEATURES }; + const result = yield { type: ACTION_TYPES.DO_REFRESH_FEATURES }; + + if ( result && result.success ) { + return yield dispatch( STORE_NAME ).refreshMerchantData(); + } + + return result; }; From f3d1e2ee9e907a558021716cdc279fdc327c7a2c Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 17 Dec 2024 16:55:30 +0400 Subject: [PATCH 136/169] Add location based descriptions on styling tab --- .../Components/Screens/Overview/TabStyling.js | 16 ++++------ .../js/data/settings/tab-styling-data.js | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js index ed967deb2..73516170d 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js @@ -90,7 +90,7 @@ const TabStyling = () => { return (
- + { ); }; -const SectionIntro = () => { - const buttonStyleDescription = sprintf( - // translators: %s: Link to Classic checkout page - __( - 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Classic Checkout page. Checkout Buttons must be enabled to display the PayPal gateway on the Checkout page.' - ), - '#' - ); +const SectionIntro = ( { location } ) => { + const { description, link } = defaultLocationSettings[ location ]; + const buttonStyleDescription = sprintf( description, link ); + return ( + /> ); }; diff --git a/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js b/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js index 5a0853f00..6bdb4f643 100644 --- a/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js +++ b/modules/ppcp-settings/resources/js/data/settings/tab-styling-data.js @@ -25,26 +25,56 @@ export const defaultLocationSettings = { value: 'cart', label: __( 'Cart', 'woocommerce-paypal-payments' ), settings: { ...cartAndExpressCheckoutSettings }, + // translators: %s: Link to Cart page + description: __( + 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Cart page and select which additional payment buttons to display in this location.', + 'wooocommerce-paypal-payments' + ), + descriptionLink: '#', }, 'classic-checkout': { value: 'classic-checkout', label: __( 'Classic Checkout', 'woocommerce-paypal-payments' ), settings: { ...settings }, + // translators: %s: Link to Classic Checkout page + description: __( + 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Classic Checkout page and choose which additional payment buttons to display in this location.', + 'wooocommerce-paypal-payments' + ), + descriptionLink: '#', }, 'express-checkout': { value: 'express-checkout', label: __( 'Express Checkout', 'woocommerce-paypal-payments' ), settings: { ...cartAndExpressCheckoutSettings }, + // translators: %s: Link to Express Checkout location + description: __( + 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Express Checkout location and choose which additional payment buttons to display in this location.', + 'wooocommerce-paypal-payments' + ), + descriptionLink: '#', }, 'mini-cart': { value: 'mini-cart', label: __( 'Mini Cart', 'woocommerce-paypel-payements' ), settings: { ...settings }, + // translators: %s: Link to Mini Cart + description: __( + 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Mini Cart and choose which additional payment buttons to display in this location.', + 'wooocommerce-paypal-payments' + ), + descriptionLink: '#', }, 'product-page': { value: 'product-page', label: __( 'Product Page', 'woocommerce-paypal-payments' ), settings: { ...settings }, + // translators: %s: Link to Product Page + description: __( + 'Customize the appearance of the PayPal smart buttons on the [MISSING LINK]Product Page and choose which additional payment buttons to display in this location.', + 'wooocommerce-paypal-payments' + ), + descriptionLink: '#', }, }; From e33302c6f7aa18dec7424d8990bfd4702d9ed59a Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 17 Dec 2024 17:33:13 +0400 Subject: [PATCH 137/169] Fix the param naming --- .../resources/js/Components/Screens/Overview/TabStyling.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js index 73516170d..7ddd8be7a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabStyling.js @@ -158,8 +158,9 @@ const TabStylingSection = ( props ) => { }; const SectionIntro = ( { location } ) => { - const { description, link } = defaultLocationSettings[ location ]; - const buttonStyleDescription = sprintf( description, link ); + const { description, descriptionLink } = + defaultLocationSettings[ location ]; + const buttonStyleDescription = sprintf( description, descriptionLink ); return ( Date: Tue, 17 Dec 2024 15:03:15 +0100 Subject: [PATCH 138/169] =?UTF-8?q?=E2=8F=AA=EF=B8=8F=20Revert=20recent=20?= =?UTF-8?q?changes=20from=20PCP-4025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SettingsBlocks/FeatureSettingsBlock.js | 78 ++++++++----------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js index f2d66e9a3..e2681a909 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js @@ -11,56 +11,42 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => { } return ( - <> - - { notes.map( ( note, index ) => ( - { note } - ) ) } - - + + { notes.map( ( note, index ) => ( + { note } + ) ) } + ); }; return ( - ( - <> -
- - { title } - { props.actionProps?.enabled && ( - <TitleBadge - { ...props.actionProps?.badge } - /> - ) } - - - { description } - { printNotes() } - -
- -
- { props.actionProps?.buttons.map( - ( button ) => ( - - ) - ) } -
-
- - ), - ] } - /> + +
+ + { title } + { props.actionProps?.enabled && ( + <TitleBadge { ...props.actionProps?.badge } /> + ) } + + + { description } + { printNotes() } + +
+ +
+ { props.actionProps?.buttons.map( ( button ) => ( + + ) ) } +
+
+
); }; From 4cbcdce75d4a9c5c81ecb551468c26c4d675e88f Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 17 Dec 2024 15:04:26 +0100 Subject: [PATCH 139/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20Icon=20compo?= =?UTF-8?q?nent=20instead=20of=20getImage()=20helper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/Screens/Overview/TabOverview.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 fb7fe30c6..1c3947449 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js @@ -1,13 +1,14 @@ import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; -import { Button } from '@wordpress/components'; +import { Button, Icon } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; +import { reusableBlock } from '@wordpress/icons'; + 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'; import { useMerchantInfo } from '../../../data/common/hooks'; -import { useDispatch } from '@wordpress/data'; import { STORE_NAME } from '../../../data/common'; const TabOverview = () => { @@ -88,7 +89,7 @@ const TabOverview = () => { onClick={ refreshHandler } disabled={ isRefreshing } > - { data().getImage( 'icon-refresh.svg' ) } + { isRefreshing ? __( 'Refreshing…', From 11599102f4668b39a62ba13ceacc00fc5e12c238 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 17 Dec 2024 15:05:05 +0100 Subject: [PATCH 140/169] =?UTF-8?q?=F0=9F=92=A1=20Add=20code=20remarks=20o?= =?UTF-8?q?n=20planned=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/Components/Screens/Overview/TabOverview.js | 3 +++ 1 file changed, 3 insertions(+) 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 1c3947449..102426d0a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js @@ -32,6 +32,7 @@ const TabOverview = () => { const result = await refreshFeatureStatuses(); + // TODO: Implement the refresh logic, remove this debug code -- PCP-4024 if ( result && ! result.success ) { console.error( 'Failed to refresh features:', @@ -128,6 +129,7 @@ const TabOverview = () => { ); }; +// TODO: This list should be refactored into a separate module, maybe utils/thingsToDoNext.js const todosDataDefault = [ { value: 'paypal_later_messaging', @@ -163,6 +165,7 @@ const todosDataDefault = [ }, ]; +// TODO: Hardcoding this list here is not the best idea. Can we move this to a REST API response? const featuresDefault = [ { id: 'save_paypal_and_venmo', From a77b209e3e2602c04c8cd713e4463a816779c2bc Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 17 Dec 2024 15:05:34 +0100 Subject: [PATCH 141/169] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20Redux?= =?UTF-8?q?=20controllers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/common/actions.js | 12 ++++- .../resources/js/data/common/controls.js | 52 ++++--------------- 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 1982d7f42..ccbf34ce0 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -187,7 +187,13 @@ export const connectViaIdAndSecret = function* () { * @return {Action} The action. */ export const refreshMerchantData = function* () { - return yield { type: ACTION_TYPES.DO_REFRESH_MERCHANT }; + const result = yield { type: ACTION_TYPES.DO_REFRESH_MERCHANT }; + + if ( result.success && result.merchant ) { + yield hydrate( result ); + } + + return result; }; /** @@ -201,7 +207,9 @@ export const refreshFeatureStatuses = function* () { const result = yield { type: ACTION_TYPES.DO_REFRESH_FEATURES }; if ( result && result.success ) { - return yield dispatch( STORE_NAME ).refreshMerchantData(); + // TODO: Review if we can get the updated feature details in the result.data instead of + // doing a second refreshMerchantData() request. + yield refreshMerchantData(); } return result; diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js index e095d6acb..a088660b9 100644 --- a/modules/ppcp-settings/resources/js/data/common/controls.js +++ b/modules/ppcp-settings/resources/js/data/common/controls.js @@ -7,11 +7,9 @@ * @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, @@ -23,7 +21,7 @@ import ACTION_TYPES from './action-types'; export const controls = { async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) { try { - return await apiFetch( { + await apiFetch( { path: REST_PERSIST_PATH, method: 'POST', data, @@ -34,10 +32,8 @@ export const controls = { }, async [ ACTION_TYPES.DO_SANDBOX_LOGIN ]() { - let result = null; - try { - result = await apiFetch( { + return apiFetch( { path: REST_CONNECTION_URL_PATH, method: 'POST', data: { @@ -46,20 +42,16 @@ export const controls = { }, } ); } catch ( e ) { - result = { + return { success: false, error: e, }; } - - return result; }, async [ ACTION_TYPES.DO_PRODUCTION_LOGIN ]( { products } ) { - let result = null; - try { - result = await apiFetch( { + return apiFetch( { path: REST_CONNECTION_URL_PATH, method: 'POST', data: { @@ -68,13 +60,11 @@ export const controls = { }, } ); } catch ( e ) { - result = { + return { success: false, error: e, }; } - - return result; }, async [ ACTION_TYPES.DO_MANUAL_CONNECTION ]( { @@ -82,10 +72,8 @@ export const controls = { clientSecret, useSandbox, } ) { - let result = null; - try { - result = await apiFetch( { + return await apiFetch( { path: REST_MANUAL_CONNECTION_PATH, method: 'POST', data: { @@ -95,54 +83,36 @@ export const controls = { }, } ); } catch ( e ) { - result = { + return { success: false, error: e, }; } - - 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 ); - } + return await apiFetch( { path: REST_HYDRATE_MERCHANT_PATH } ); } catch ( e ) { - result = { + return { success: false, error: e, }; } - - return result; }, async [ ACTION_TYPES.DO_REFRESH_FEATURES ]() { - let result = null; - try { - result = await apiFetch( { + return await apiFetch( { path: REST_REFRESH_FEATURES_PATH, method: 'POST', } ); - - if ( result.success ) { - result = await dispatch( STORE_NAME ).refreshMerchantData(); - } } catch ( e ) { - result = { + return { success: false, error: e, message: e.message, }; } - - return result; }, }; From 08f5b4fba56f4308c309aaaa14fbe60fbac1a113 Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Wed, 18 Dec 2024 07:00:47 +0100 Subject: [PATCH 142/169] Get and resubscribe webhooks --- .../SettingsBlocks/ButtonSettingsBlock.js | 1 + .../Blocks/Troubleshooting.js | 68 +++----- .../resources/js/data/common/action-types.js | 1 + .../resources/js/data/common/actions.js | 5 + .../resources/js/data/common/constants.js | 2 + .../resources/js/data/common/hooks.js | 30 +++- .../resources/js/data/common/reducer.js | 1 + .../resources/js/data/common/resolvers.js | 4 +- .../resources/js/data/common/selectors.js | 4 + modules/ppcp-settings/services.php | 8 + .../src/Endpoint/CommonRestEndpoint.php | 3 + .../src/Endpoint/WebhookSettingsEndpoint.php | 152 ++++-------------- modules/ppcp-settings/src/SettingsModule.php | 1 + 13 files changed, 113 insertions(+), 167 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js index e2b2aeb53..6cdadb43c 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js @@ -15,6 +15,7 @@ const ButtonSettingsBlock = ( { title, description, ...props } ) => ( -
+ } contentItems={ features.map( ( feature ) => ( { /> ) ) } /> + + , + , + ] } + />
); }; From 0e469db58df123106e20d47d6dca1ae0972fc186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCsken?= Date: Wed, 18 Dec 2024 12:15:33 +0100 Subject: [PATCH 146/169] script translations must be configured additional when not used directly in a block --- modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index c52d7e601..222d53e22 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -97,11 +97,16 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { wp_register_script( 'ppcp-advanced-card-checkout-block', trailingslashit( $this->module_url ) . 'assets/js/advanced-card-checkout-block.js', - array(), + array( 'wp-i18n' ), $this->version, true ); + wp_set_script_translations( + 'ppcp-advanced-card-checkout-block', + 'woocommerce-paypal-payments' + ); + return array( 'ppcp-advanced-card-checkout-block' ); } From cb8c3f8726fd8895acf6fc87e4650246e1b8e653 Mon Sep 17 00:00:00 2001 From: Wesley Rosa Date: Wed, 18 Dec 2024 13:40:58 -0300 Subject: [PATCH 147/169] Updating GitHub upload action due deprecation (#2921) --- .github/workflows/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index e7957d210..c3e07d1bb 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -50,7 +50,7 @@ jobs: if: github.event.inputs.filePrefix - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.FILENAME }} path: dist/ From ed77ad63ca7bedf13a7aaec15ecf0af83ac928ce Mon Sep 17 00:00:00 2001 From: inpsyde-maticluznar Date: Thu, 19 Dec 2024 13:10:19 +0100 Subject: [PATCH 148/169] Add messages, separate components, finish actions --- .../SettingsBlocks/ButtonSettingsBlock.js | 5 +- .../Blocks/Troubleshooting.js | 150 ------------------ .../Blocks/Troubleshooting/Troubleshooting.js | 75 +++++++++ .../TroubleshootingResubscribeBlock.js | 67 ++++++++ .../TroubleshootingSimulationBlock.js | 130 +++++++++++++++ .../TroubleshootingTableBlock.js | 34 ++++ .../TabSettingsElements/ExpertSettings.js | 2 +- .../resources/js/data/common/action-types.js | 3 + .../resources/js/data/common/actions.js | 34 +++- .../resources/js/data/common/constants.js | 20 ++- .../resources/js/data/common/controls.js | 12 +- .../resources/js/data/common/hooks.js | 33 ++-- .../src/Endpoint/WebhookSettingsEndpoint.php | 39 ++++- 13 files changed, 417 insertions(+), 187 deletions(-) delete mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting/Troubleshooting.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting/TroubleshootingResubscribeBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting/TroubleshootingSimulationBlock.js create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Troubleshooting/TroubleshootingTableBlock.js diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js index 1242df032..3b48b820b 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/ButtonSettingsBlock.js @@ -1,6 +1,6 @@ import { Button } from '@wordpress/components'; import SettingsBlock from './SettingsBlock'; -import { Header, Title, Action, Description } from './SettingsBlockElements'; +import { Action, Description, Header, Title } from './SettingsBlockElements'; const ButtonSettingsBlock = ( { title, description, ...props } ) => ( @@ -9,6 +9,9 @@ const ButtonSettingsBlock = ( { title, description, ...props } ) => ( { description } + { props.actionProps?.message && ( +

{ props.actionProps.message }

+ ) }