diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 482557327..155ab5d7b 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -92,6 +92,24 @@ class ApplePayButton extends PaymentButton { */ #product = {}; + /** + * The start time of the configuration process. + * @type {number} + */ + #configureStartTime = 0; + + /** + * The maximum time to wait for buttonAttributes before proceeding with initialization. + * @type {number} + */ + #maxWaitTime = 1000; + + /** + * The stored button attributes. + * @type {Object|null} + */ + #storedButtonAttributes = null; + /** * @inheritDoc */ @@ -125,7 +143,8 @@ class ApplePayButton extends PaymentButton { externalHandler, buttonConfig, ppcpConfig, - contextHandler + contextHandler, + buttonAttributes ) { // Disable debug output in the browser console: // buttonConfig.is_debug = false; @@ -135,7 +154,8 @@ class ApplePayButton extends PaymentButton { externalHandler, buttonConfig, ppcpConfig, - contextHandler + contextHandler, + buttonAttributes ); this.init = this.init.bind( this ); @@ -220,6 +240,20 @@ class ApplePayButton extends PaymentButton { 'No transactionInfo - missing configure() call?' ); + invalidIf( + () => + this.buttonAttributes?.height && + isNaN( parseInt( this.buttonAttributes.height ) ), + 'Invalid height in buttonAttributes' + ); + + invalidIf( + () => + this.buttonAttributes?.borderRadius && + isNaN( parseInt( this.buttonAttributes.borderRadius ) ), + 'Invalid borderRadius in buttonAttributes' + ); + invalidIf( () => ! this.contextHandler?.validateContext(), `Invalid context handler.` @@ -229,12 +263,60 @@ class ApplePayButton extends PaymentButton { /** * Configures the button instance. Must be called before the initial `init()`. * - * @param {Object} apiConfig - API configuration. - * @param {TransactionInfo} transactionInfo - Transaction details. + * @param {Object} apiConfig - API configuration. + * @param {TransactionInfo} transactionInfo - Transaction details. + * @param {Object} buttonAttributes - Button attributes. */ - configure( apiConfig, transactionInfo ) { + configure( apiConfig, transactionInfo, buttonAttributes = {} ) { + // Start timing on first configure call + if ( ! this.#configureStartTime ) { + this.#configureStartTime = Date.now(); + } + + // If valid buttonAttributes, store them + if ( buttonAttributes?.height && buttonAttributes?.borderRadius ) { + this.#storedButtonAttributes = { ...buttonAttributes }; + } + + // Use stored attributes if current ones are missing + const attributes = buttonAttributes?.height + ? buttonAttributes + : this.#storedButtonAttributes; + + // Check if we've exceeded wait time + const timeWaited = Date.now() - this.#configureStartTime; + if ( timeWaited > this.#maxWaitTime ) { + this.log( + 'ApplePay: Timeout waiting for buttonAttributes - proceeding with initialization' + ); + this.#applePayConfig = apiConfig; + this.#transactionInfo = transactionInfo; + this.buttonAttributes = attributes || buttonAttributes; + this.init(); + return; + } + + // Block any initialization until we have valid buttonAttributes + if ( ! attributes?.height || ! attributes?.borderRadius ) { + setTimeout( + () => + this.configure( + apiConfig, + transactionInfo, + buttonAttributes + ), + 100 + ); + return; + } + + // Reset timer for future configure calls + this.#configureStartTime = 0; + this.#applePayConfig = apiConfig; this.#transactionInfo = transactionInfo; + this.buttonAttributes = attributes; + this.init(); } init() { @@ -321,17 +403,43 @@ class ApplePayButton extends PaymentButton { applyWrapperStyles() { super.applyWrapperStyles(); - const { height } = this.style; + const wrapper = this.wrapperElement; + if ( ! wrapper ) { + return; + } - if ( height ) { - const wrapper = this.wrapperElement; + // Try stored attributes if current ones are missing + const attributes = + this.buttonAttributes?.height || this.buttonAttributes?.borderRadius + ? this.buttonAttributes + : this.#storedButtonAttributes; + const defaultHeight = 48; + const defaultBorderRadius = 4; + + const height = attributes?.height + ? parseInt( attributes.height, 10 ) + : defaultHeight; + + if ( ! isNaN( height ) ) { wrapper.style.setProperty( '--apple-pay-button-height', `${ height }px` ); - wrapper.style.height = `${ height }px`; + } else { + wrapper.style.setProperty( + '--apple-pay-button-height', + `${ defaultHeight }px` + ); + wrapper.style.height = `${ defaultHeight }px`; + } + + const borderRadius = attributes?.borderRadius + ? parseInt( attributes.borderRadius, 10 ) + : defaultBorderRadius; + if ( ! isNaN( borderRadius ) ) { + wrapper.style.borderRadius = `${ borderRadius }px`; } } @@ -342,12 +450,23 @@ class ApplePayButton extends PaymentButton { addButton() { const { color, type, language } = this.style; + // If current buttonAttributes are missing, try to use stored ones + if ( + ! this.buttonAttributes?.height && + this.#storedButtonAttributes?.height + ) { + this.buttonAttributes = { ...this.#storedButtonAttributes }; + } + const button = document.createElement( 'apple-pay-button' ); button.id = 'apple-' + this.wrapperId; + button.setAttribute( 'buttonstyle', color ); button.setAttribute( 'type', type ); button.setAttribute( 'locale', language ); + button.style.display = 'block'; + button.addEventListener( 'click', ( evt ) => { evt.preventDefault(); this.onButtonClick(); diff --git a/modules/ppcp-applepay/resources/js/ApplepayManager.js b/modules/ppcp-applepay/resources/js/ApplepayManager.js index 673078121..794464f70 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayManager.js +++ b/modules/ppcp-applepay/resources/js/ApplepayManager.js @@ -3,65 +3,83 @@ import ApplePayButton from './ApplepayButton'; import ContextHandlerFactory from './Context/ContextHandlerFactory'; class ApplePayManager { - #namespace = ''; - #buttonConfig = null; - #ppcpConfig = null; - #applePayConfig = null; - #contextHandler = null; - #transactionInfo = null; - #buttons = []; + constructor( namespace, buttonConfig, ppcpConfig, buttonAttributes = {} ) { + this.namespace = namespace; + this.buttonConfig = buttonConfig; + this.ppcpConfig = ppcpConfig; + this.buttonAttributes = buttonAttributes; + this.applePayConfig = null; + this.transactionInfo = null; + this.contextHandler = null; - constructor( namespace, buttonConfig, ppcpConfig ) { - this.#namespace = namespace; - this.#buttonConfig = buttonConfig; - this.#ppcpConfig = ppcpConfig; + this.buttons = []; - this.onContextBootstrap = this.onContextBootstrap.bind( this ); - buttonModuleWatcher.watchContextBootstrap( this.onContextBootstrap ); - } + buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => { + this.contextHandler = ContextHandlerFactory.create( + bootstrap.context, + buttonConfig, + ppcpConfig, + bootstrap.handler + ); - async onContextBootstrap( bootstrap ) { - this.#contextHandler = ContextHandlerFactory.create( - bootstrap.context, - this.#buttonConfig, - this.#ppcpConfig, - bootstrap.handler - ); + const button = ApplePayButton.createButton( + bootstrap.context, + bootstrap.handler, + buttonConfig, + ppcpConfig, + this.contextHandler, + this.buttonAttributes + ); - const button = ApplePayButton.createButton( - bootstrap.context, - bootstrap.handler, - this.#buttonConfig, - this.#ppcpConfig, - this.#contextHandler - ); + this.buttons.push( button ); + const initButton = () => { + button.configure( + this.applePayConfig, + this.transactionInfo, + this.buttonAttributes + ); + button.init(); + }; - this.#buttons.push( button ); + // Initialize button only if applePayConfig and transactionInfo are already fetched. + if ( this.applePayConfig && this.transactionInfo ) { + initButton(); + } else { + // Ensure ApplePayConfig is loaded before proceeding. + await this.init(); - // Ensure ApplePayConfig is loaded before proceeding. - await this.init(); - - button.configure( this.#applePayConfig, this.#transactionInfo ); - button.init(); + if ( this.applePayConfig && this.transactionInfo ) { + initButton(); + } + } + } ); } async init() { try { - if ( ! this.#applePayConfig ) { - this.#applePayConfig = await window[ this.#namespace ] + if ( ! this.applePayConfig ) { + // Gets ApplePay configuration of the PayPal merchant. + this.applePayConfig = await window[ this.namespace ] .Applepay() .config(); - - if ( ! this.#applePayConfig ) { - console.error( 'No ApplePayConfig received during init' ); - } } - if ( ! this.#transactionInfo ) { - this.#transactionInfo = await this.fetchTransactionInfo(); + if ( ! this.transactionInfo ) { + this.transactionInfo = await this.fetchTransactionInfo(); + } - if ( ! this.#applePayConfig ) { - console.error( 'No transactionInfo found during init' ); + if ( ! this.applePayConfig ) { + console.error( 'No ApplePayConfig received during init' ); + } else if ( ! this.transactionInfo ) { + console.error( 'No transactionInfo found during init' ); + } else { + for ( const button of this.buttons ) { + button.configure( + this.applePayConfig, + this.transactionInfo, + this.buttonAttributes + ); + button.init(); } } } catch ( error ) { @@ -71,10 +89,10 @@ class ApplePayManager { async fetchTransactionInfo() { try { - if ( ! this.#contextHandler ) { + if ( ! this.contextHandler ) { throw new Error( 'ContextHandler is not initialized' ); } - return await this.#contextHandler.transactionInfo(); + return await this.contextHandler.transactionInfo(); } catch ( error ) { console.error( 'Error fetching transaction info:', error ); throw error; @@ -82,7 +100,7 @@ class ApplePayManager { } reinit() { - for ( const button of this.#buttons ) { + for ( const button of this.buttons ) { button.reinit(); } } diff --git a/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js b/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js index 0d60929e4..3381baf93 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js +++ b/modules/ppcp-applepay/resources/js/ApplepayManagerBlockEditor.js @@ -4,11 +4,13 @@ const ApplePayManagerBlockEditor = ( { namespace, buttonConfig, ppcpConfig, + buttonAttributes, } ) => ( ); diff --git a/modules/ppcp-applepay/resources/js/Block/components/ApplePayButton.js b/modules/ppcp-applepay/resources/js/Block/components/ApplePayButton.js index c01f04320..8b4985a1f 100644 --- a/modules/ppcp-applepay/resources/js/Block/components/ApplePayButton.js +++ b/modules/ppcp-applepay/resources/js/Block/components/ApplePayButton.js @@ -4,7 +4,12 @@ import usePayPalScript from '../hooks/usePayPalScript'; import useApplepayScript from '../hooks/useApplepayScript'; import useApplepayConfig from '../hooks/useApplepayConfig'; -const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => { +const ApplepayButton = ( { + namespace, + buttonConfig, + ppcpConfig, + buttonAttributes, +} ) => { const [ buttonHtml, setButtonHtml ] = useState( '' ); const [ buttonElement, setButtonElement ] = useState( null ); const [ componentFrame, setComponentFrame ] = useState( null ); @@ -31,19 +36,42 @@ const ApplepayButton = ( { namespace, buttonConfig, ppcpConfig } ) => { namespace, buttonConfig, ppcpConfig, - applepayConfig + applepayConfig, + buttonAttributes ); useEffect( () => { - if ( applepayButton ) { - setButtonHtml( applepayButton.outerHTML ); + if ( ! applepayButton || ! buttonElement ) { + return; } - }, [ applepayButton ] ); + + setButtonHtml( applepayButton.outerHTML ); + + // Add timeout to ensure button is displayed after render + setTimeout( () => { + const button = buttonElement.querySelector( 'apple-pay-button' ); + if ( button ) { + button.style.display = 'block'; + } + }, 100 ); // Add a small delay to ensure DOM is ready + }, [ applepayButton, buttonElement ] ); return (
); }; diff --git a/modules/ppcp-applepay/resources/js/boot-block.js b/modules/ppcp-applepay/resources/js/boot-block.js index 8013177d0..eff026251 100644 --- a/modules/ppcp-applepay/resources/js/boot-block.js +++ b/modules/ppcp-applepay/resources/js/boot-block.js @@ -19,7 +19,7 @@ if ( typeof window.PayPalCommerceGateway === 'undefined' ) { window.PayPalCommerceGateway = ppcpConfig; } -const ApplePayComponent = ( { isEditing } ) => { +const ApplePayComponent = ( { isEditing, buttonAttributes } ) => { const [ paypalLoaded, setPaypalLoaded ] = useState( false ); const [ applePayLoaded, setApplePayLoaded ] = useState( false ); const wrapperRef = useRef( null ); @@ -57,8 +57,13 @@ const ApplePayComponent = ( { isEditing } ) => { buttonConfig.reactWrapper = wrapperRef.current; - new ManagerClass( namespace, buttonConfig, ppcpConfig ); - }, [ paypalLoaded, applePayLoaded, isEditing ] ); + new ManagerClass( + namespace, + buttonConfig, + ppcpConfig, + buttonAttributes + ); + }, [ paypalLoaded, applePayLoaded, isEditing, buttonAttributes ] ); if ( isEditing ) { return ( @@ -66,6 +71,7 @@ const ApplePayComponent = ( { isEditing } ) => { namespace={ namespace } buttonConfig={ buttonConfig } ppcpConfig={ ppcpConfig } + buttonAttributes={ buttonAttributes } /> ); } @@ -102,5 +108,6 @@ registerExpressPaymentMethod( { canMakePayment: () => buttonData.enabled, supports: { features, + style: [ 'height', 'borderRadius' ], }, } ); diff --git a/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js new file mode 100644 index 000000000..b831bd45b --- /dev/null +++ b/modules/ppcp-axo-block/resources/js/plugins/PayPalInsightsLoader.js @@ -0,0 +1,259 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useEffect, useCallback, useState, useRef } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { PAYMENT_STORE_KEY } from '@woocommerce/block-data'; +import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights'; +import { STORE_NAME } from '../stores/axoStore'; +import usePayPalCommerceGateway from '../hooks/usePayPalCommerceGateway'; + +const GATEWAY_HANDLE = 'ppcp-axo-gateway'; + +const useEventTracking = () => { + const [ triggeredEvents, setTriggeredEvents ] = useState( { + initialized: false, + jsLoaded: false, + beginCheckout: false, + emailSubmitted: false, + } ); + + const currentPaymentMethod = useRef( null ); + + const setEventTriggered = useCallback( ( eventName, value = true ) => { + setTriggeredEvents( ( prev ) => ( { + ...prev, + [ eventName ]: value, + } ) ); + }, [] ); + + const isEventTriggered = useCallback( + ( eventName ) => triggeredEvents[ eventName ], + [ triggeredEvents ] + ); + + const setCurrentPaymentMethod = useCallback( ( methodName ) => { + currentPaymentMethod.current = methodName; + }, [] ); + + const getCurrentPaymentMethod = useCallback( + () => currentPaymentMethod.current, + [] + ); + + return { + setEventTriggered, + isEventTriggered, + setCurrentPaymentMethod, + getCurrentPaymentMethod, + }; +}; + +const waitForPayPalInsight = () => { + return new Promise( ( resolve, reject ) => { + // If already loaded, resolve immediately + if ( window.paypalInsight ) { + resolve( window.paypalInsight ); + return; + } + + // Set a reasonable timeout + const timeoutId = setTimeout( () => { + observer.disconnect(); + reject( new Error( 'PayPal Insights script load timeout' ) ); + }, 10000 ); + + // Create MutationObserver to watch for script initialization + const observer = new MutationObserver( () => { + if ( window.paypalInsight ) { + observer.disconnect(); + clearTimeout( timeoutId ); + resolve( window.paypalInsight ); + } + } ); + + // Start observing + observer.observe( document, { + childList: true, + subtree: true, + } ); + } ); +}; + +const usePayPalInsightsInit = ( axoConfig, ppcpConfig, eventTracking ) => { + const { setEventTriggered, isEventTriggered } = eventTracking; + const initialized = useRef( false ); + + useEffect( () => { + if ( + ! axoConfig?.insights?.enabled || + ! axoConfig?.insights?.client_id || + ! axoConfig?.insights?.session_id || + initialized.current || + isEventTriggered( 'initialized' ) + ) { + return; + } + + const initializePayPalInsights = async () => { + try { + await waitForPayPalInsight(); + + if ( initialized.current ) { + return; + } + + // Track JS load first + PayPalInsights.trackJsLoad(); + setEventTriggered( 'jsLoaded' ); + + PayPalInsights.config( axoConfig.insights.client_id, { + debug: axoConfig?.wp_debug === '1', + } ); + + PayPalInsights.setSessionId( axoConfig.insights.session_id ); + initialized.current = true; + setEventTriggered( 'initialized' ); + + if ( + isEventTriggered( 'jsLoaded' ) && + ! isEventTriggered( 'beginCheckout' ) + ) { + PayPalInsights.trackBeginCheckout( { + amount: axoConfig.insights.amount, + page_type: 'checkout', + user_data: { + country: 'US', + is_store_member: false, + }, + } ); + setEventTriggered( 'beginCheckout' ); + } + } catch ( error ) { + console.error( + 'PayPal Insights initialization failed:', + error + ); + } + }; + + initializePayPalInsights(); + + return () => { + initialized.current = false; + }; + }, [ axoConfig, ppcpConfig, setEventTriggered, isEventTriggered ] ); +}; + +const usePaymentMethodTracking = ( axoConfig, eventTracking ) => { + const { setCurrentPaymentMethod } = eventTracking; + const lastPaymentMethod = useRef( null ); + const isInitialMount = useRef( true ); + + const activePaymentMethod = useSelect( ( select ) => { + return select( PAYMENT_STORE_KEY )?.getActivePaymentMethod(); + }, [] ); + + const handlePaymentMethodChange = useCallback( + async ( paymentMethod ) => { + // Skip if no payment method or same as last one + if ( + ! paymentMethod || + paymentMethod === lastPaymentMethod.current + ) { + return; + } + + try { + await waitForPayPalInsight(); + + // Only track if it's not the initial mount, and we have a previous payment method + if ( ! isInitialMount.current && lastPaymentMethod.current ) { + PayPalInsights.trackSelectPaymentMethod( { + payment_method_selected: + axoConfig?.insights?.payment_method_selected_map[ + paymentMethod + ] || 'other', + page_type: 'checkout', + } ); + } + + lastPaymentMethod.current = paymentMethod; + setCurrentPaymentMethod( paymentMethod ); + } catch ( error ) { + console.error( 'Failed to track payment method:', error ); + } + }, + [ + axoConfig?.insights?.payment_method_selected_map, + setCurrentPaymentMethod, + ] + ); + + useEffect( () => { + if ( activePaymentMethod ) { + if ( isInitialMount.current ) { + // Just set the initial payment method without tracking + lastPaymentMethod.current = activePaymentMethod; + setCurrentPaymentMethod( activePaymentMethod ); + isInitialMount.current = false; + } else { + handlePaymentMethodChange( activePaymentMethod ); + } + } + }, [ + activePaymentMethod, + handlePaymentMethodChange, + setCurrentPaymentMethod, + ] ); + + useEffect( () => { + return () => { + lastPaymentMethod.current = null; + isInitialMount.current = true; + }; + }, [] ); +}; + +const PayPalInsightsLoader = () => { + const eventTracking = useEventTracking(); + const { setEventTriggered, isEventTriggered } = eventTracking; + + const initialConfig = + window?.wc?.wcSettings?.getSetting( `${ GATEWAY_HANDLE }_data` ) || {}; + + const { ppcpConfig } = usePayPalCommerceGateway( initialConfig ); + const axoConfig = window?.wc_ppcp_axo; + + const { isEmailSubmitted } = useSelect( ( select ) => { + const storeSelect = select( STORE_NAME ); + return { + isEmailSubmitted: storeSelect?.getIsEmailSubmitted?.() ?? false, + }; + }, [] ); + + usePayPalInsightsInit( axoConfig, ppcpConfig, eventTracking ); + usePaymentMethodTracking( axoConfig, eventTracking ); + + useEffect( () => { + const trackEmail = async () => { + if ( isEmailSubmitted && ! isEventTriggered( 'emailSubmitted' ) ) { + try { + await waitForPayPalInsight(); + PayPalInsights.trackSubmitCheckoutEmail(); + setEventTriggered( 'emailSubmitted' ); + } catch ( error ) { + console.error( 'Failed to track email submission:', error ); + } + } + }; + trackEmail(); + }, [ isEmailSubmitted, setEventTriggered, isEventTriggered ] ); + + return null; +}; + +registerPlugin( 'wc-ppcp-paypal-insights', { + render: PayPalInsightsLoader, + scope: 'woocommerce-checkout', +} ); + +export default PayPalInsightsLoader; diff --git a/modules/ppcp-axo-block/resources/js/stores/axoStore.js b/modules/ppcp-axo-block/resources/js/stores/axoStore.js index c7afb91ef..c9c5449a4 100644 --- a/modules/ppcp-axo-block/resources/js/stores/axoStore.js +++ b/modules/ppcp-axo-block/resources/js/stores/axoStore.js @@ -1,4 +1,4 @@ -import { createReduxStore, register, dispatch } from '@wordpress/data'; +import { createReduxStore, register, dispatch, select } from '@wordpress/data'; export const STORE_NAME = 'woocommerce-paypal-payments/axo-block'; @@ -108,13 +108,15 @@ const selectors = { }; // Create and register the Redux store for the AXO block -const store = createReduxStore( STORE_NAME, { - reducer, - actions, - selectors, -} ); +if ( ! select( STORE_NAME ) ) { + const store = createReduxStore( STORE_NAME, { + reducer, + actions, + selectors, + } ); -register( store ); + register( store ); +} // Action dispatchers diff --git a/modules/ppcp-axo-block/services.php b/modules/ppcp-axo-block/services.php index 61a3c77a7..f91cd738a 100644 --- a/modules/ppcp-axo-block/services.php +++ b/modules/ppcp-axo-block/services.php @@ -33,11 +33,12 @@ return array( $container->get( 'axoblock.url' ), $container->get( 'ppcp.asset-version' ), $container->get( 'axo.gateway' ), - fn() : SmartButtonInterface => $container->get( 'button.smart-button' ), + fn(): SmartButtonInterface => $container->get( 'button.smart-button' ), $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.configuration.dcc' ), $container->get( 'onboarding.environment' ), $container->get( 'wcgateway.url' ), + $container->get( 'axo.payment_method_selected_map' ), $container->get( 'axo.supported-country-card-type-matrix' ), $container->get( 'axo.shipping-wc-enabled-locations' ) ); diff --git a/modules/ppcp-axo-block/src/AxoBlockModule.php b/modules/ppcp-axo-block/src/AxoBlockModule.php index 669cc7cc5..1ebb068ed 100644 --- a/modules/ppcp-axo-block/src/AxoBlockModule.php +++ b/modules/ppcp-axo-block/src/AxoBlockModule.php @@ -133,6 +133,15 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule wp_enqueue_style( 'wc-ppcp-axo-block' ); } ); + + // Enqueue the PayPal Insights script. + add_action( + 'wp_enqueue_scripts', + function () use ( $c ) { + $this->enqueue_paypal_insights_script( $c ); + } + ); + return true; } @@ -166,4 +175,37 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule return $localized_script_data; } + + /** + * Enqueues PayPal Insights analytics script for the Checkout block. + * + * @param ContainerInterface $c The service container. + * @return void + */ + private function enqueue_paypal_insights_script( ContainerInterface $c ): void { + if ( ! has_block( 'woocommerce/checkout' ) || WC()->cart->is_empty() ) { + return; + } + + $module_url = $c->get( 'axoblock.url' ); + $asset_version = $c->get( 'ppcp.asset-version' ); + + wp_register_script( + 'wc-ppcp-paypal-insights', + untrailingslashit( $module_url ) . '/assets/js/PayPalInsightsLoader.js', + array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ), + $asset_version, + true + ); + + wp_localize_script( + 'wc-ppcp-paypal-insights', + 'ppcpPayPalInsightsData', + array( + 'isAxoEnabled' => true, + ) + ); + + wp_enqueue_script( 'wc-ppcp-paypal-insights' ); + } } diff --git a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php index 373c61023..fa546d5ac 100644 --- a/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php +++ b/modules/ppcp-axo-block/src/AxoBlockPaymentMethod.php @@ -72,6 +72,13 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { */ private $environment; + /** + * Mapping of payment methods to the PayPal Insights 'payment_method_selected' types. + * + * @var array + */ + private array $payment_method_selected_map; + /** * The WcGateway module URL. * @@ -96,29 +103,30 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { /** * AdvancedCardPaymentMethod constructor. * - * @param string $module_url The URL of this module. - * @param string $version The assets version. - * @param WC_Payment_Gateway $gateway Credit card gateway. - * @param SmartButtonInterface|callable $smart_button The smart button script loading - * handler. - * @param Settings $settings The settings. - * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. - * @param Environment $environment The environment object. + * @param string $module_url The URL of this module. + * @param string $version The assets version. + * @param WC_Payment_Gateway $gateway Credit card gateway. + * @param SmartButtonInterface|callable $smart_button The smart button script loading handler. + * @param Settings $settings The settings. + * @param DCCGatewayConfiguration $dcc_configuration The DCC gateway settings. + * @param Environment $environment The environment object. * @param string $wcgateway_module_url The WcGateway module URL. + * @param array $payment_method_selected_map Mapping of payment methods to the PayPal Insights 'payment_method_selected' types. * @param array $supported_country_card_type_matrix The supported country card type matrix for Axo. * @param array $enabled_shipping_locations The list of WooCommerce enabled shipping locations. */ public function __construct( - string $module_url, - string $version, - WC_Payment_Gateway $gateway, - $smart_button, - Settings $settings, - DCCGatewayConfiguration $dcc_configuration, - Environment $environment, - string $wcgateway_module_url, - array $supported_country_card_type_matrix, - array $enabled_shipping_locations + string $module_url, + string $version, + WC_Payment_Gateway $gateway, + $smart_button, + Settings $settings, + DCCGatewayConfiguration $dcc_configuration, + Environment $environment, + string $wcgateway_module_url, + array $payment_method_selected_map, + array $supported_country_card_type_matrix, + array $enabled_shipping_locations ) { $this->name = AxoGateway::ID; $this->module_url = $module_url; @@ -129,10 +137,10 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { $this->dcc_configuration = $dcc_configuration; $this->environment = $environment; $this->wcgateway_module_url = $wcgateway_module_url; + $this->payment_method_selected_map = $payment_method_selected_map; $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; $this->enabled_shipping_locations = $enabled_shipping_locations; } - /** * {@inheritDoc} */ @@ -213,18 +221,19 @@ class AxoBlockPaymentMethod extends AbstractPaymentMethodType { 'email' => 'render', ), 'insights' => array( - 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), - 'session_id' => + 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), + 'session_id' => ( WC()->session && method_exists( WC()->session, 'get_customer_unique_id' ) ) ? substr( md5( WC()->session->get_customer_unique_id() ), 0, 16 ) : '', - 'amount' => array( + 'amount' => array( 'currency_code' => get_woocommerce_currency(), 'value' => ( WC()->cart && method_exists( WC()->cart, 'get_total' ) ) ? WC()->cart->get_total( 'numeric' ) - : null, // Set to null if WC()->cart is null or get_total doesn't exist. + : null, ), + 'payment_method_selected_map' => $this->payment_method_selected_map, ), 'allowed_cards' => $this->supported_country_card_type_matrix, 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), diff --git a/modules/ppcp-axo-block/webpack.config.js b/modules/ppcp-axo-block/webpack.config.js index b5db53234..86e2e2087 100644 --- a/modules/ppcp-axo-block/webpack.config.js +++ b/modules/ppcp-axo-block/webpack.config.js @@ -9,7 +9,10 @@ module.exports = { target: 'web', plugins: [ new DependencyExtractionWebpackPlugin() ], entry: { - 'index': path.resolve( './resources/js/index.js' ), + index: path.resolve( './resources/js/index.js' ), + PayPalInsightsLoader: path.resolve( + './resources/js/plugins/PayPalInsightsLoader.js' + ), gateway: path.resolve( './resources/css/gateway.scss' ), }, output: { diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 51dd34147..e93fa9527 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -121,7 +121,7 @@ class AxoManager { this.axoConfig?.insights?.session_id ) { PayPalInsights.config( this.axoConfig?.insights?.client_id, { - debug: true, + debug: axoConfig?.wp_debug === '1', } ); PayPalInsights.setSessionId( this.axoConfig?.insights?.session_id ); PayPalInsights.trackJsLoad(); @@ -164,19 +164,25 @@ class AxoManager { } registerEventHandlers() { + // Payment method change tracking with duplicate prevention + let lastSelectedPaymentMethod = document.querySelector( + 'input[name=payment_method]:checked' + )?.value; this.$( document ).on( 'change', 'input[name=payment_method]', ( ev ) => { - const map = { - 'ppcp-axo-gateway': 'card', - 'ppcp-gateway': 'paypal', - }; - - PayPalInsights.trackSelectPaymentMethod( { - payment_method_selected: map[ ev.target.value ] || 'other', - page_type: 'checkout', - } ); + if ( lastSelectedPaymentMethod !== ev.target.value ) { + PayPalInsights.trackSelectPaymentMethod( { + payment_method_selected: + this.axoConfig?.insights + ?.payment_method_selected_map[ + ev.target.value + ] || 'other', + page_type: 'checkout', + } ); + lastSelectedPaymentMethod = ev.target.value; + } } ); @@ -1166,16 +1172,6 @@ class AxoManager { this.el.axoNonceInput.get().value = nonce; - PayPalInsights.trackEndCheckout( { - amount: this.axoConfig?.insights?.amount, - page_type: 'checkout', - payment_method_selected: 'card', - user_data: { - country: 'US', - is_store_member: false, - }, - } ); - if ( data ) { // Ryan flow. const form = document.querySelector( 'form.woocommerce-checkout' ); diff --git a/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js b/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js new file mode 100644 index 000000000..e21a9d0e4 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Insights/EndCheckoutTracker.js @@ -0,0 +1,99 @@ +import PayPalInsights from '../../../../ppcp-axo/resources/js/Insights/PayPalInsights'; + +class EndCheckoutTracker { + constructor() { + this.initialize(); + } + + async initialize() { + const axoConfig = window.wc_ppcp_axo_insights_data || {}; + + if ( + axoConfig?.enabled === '1' && + axoConfig?.client_id && + axoConfig?.session_id && + axoConfig?.orderTotal && + axoConfig?.orderCurrency + ) { + try { + await this.waitForPayPalInsight(); + + PayPalInsights.config( axoConfig?.client_id, { + debug: axoConfig?.wp_debug === '1', + } ); + PayPalInsights.setSessionId( axoConfig.session_id ); + PayPalInsights.trackJsLoad(); + + const trackingData = { + amount: { + currency_code: axoConfig?.orderCurrency, + value: axoConfig?.orderTotal, + }, + page_type: 'checkout', + payment_method_selected: + axoConfig?.payment_method_selected_map[ + axoConfig?.paymentMethod + ] || 'other', + user_data: { + country: 'US', + is_store_member: false, + }, + order_id: axoConfig?.orderId, + order_key: axoConfig?.orderKey, + }; + + PayPalInsights.trackEndCheckout( trackingData ); + } catch ( error ) { + console.error( + 'EndCheckoutTracker: Error during tracking:', + error + ); + console.error( 'PayPalInsights object:', window.paypalInsight ); + } + } else { + console.warn( + 'EndCheckoutTracker: Missing required configuration', + { + enabled: axoConfig?.enabled, + hasClientId: !! axoConfig?.client_id, + hasSessionId: !! axoConfig?.session_id, + hasOrderTotal: !! axoConfig?.orderTotal, + hasOrderCurrency: !! axoConfig?.orderCurrency, + } + ); + } + } + + waitForPayPalInsight() { + return new Promise( ( resolve, reject ) => { + // If already loaded, resolve immediately + if ( window.paypalInsight ) { + resolve( window.paypalInsight ); + return; + } + + const timeoutId = setTimeout( () => { + observer.disconnect(); + reject( new Error( 'PayPal Insights script load timeout' ) ); + }, 10000 ); + + // Create MutationObserver to watch for script initialization + const observer = new MutationObserver( () => { + if ( window.paypalInsight ) { + observer.disconnect(); + clearTimeout( timeoutId ); + resolve( window.paypalInsight ); + } + } ); + + observer.observe( document, { + childList: true, + subtree: true, + } ); + } ); + } +} + +document.addEventListener( 'DOMContentLoaded', () => { + new EndCheckoutTracker(); +} ); diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index e93df1721..e6767a29a 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration; +use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter; return array( @@ -64,6 +65,7 @@ return array( $container->get( 'session.handler' ), $container->get( 'wcgateway.settings' ), $container->get( 'onboarding.environment' ), + $container->get( 'axo.insights' ), $container->get( 'wcgateway.settings.status' ), $container->get( 'api.shop.currency.getter' ), $container->get( 'woocommerce.logger.woocommerce' ), @@ -91,6 +93,55 @@ return array( ); }, + // Data needed for the PayPal Insights. + 'axo.insights' => static function ( ContainerInterface $container ): array { + $settings = $container->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + $currency = $container->get( 'api.shop.currency.getter' ); + assert( $currency instanceof CurrencyGetter ); + + $session_id = ''; + if ( isset( WC()->session ) && method_exists( WC()->session, 'get_customer_unique_id' ) ) { + $session_id = substr( + md5( WC()->session->get_customer_unique_id() ), + 0, + 16 + ); + } + + return array( + 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, + 'client_id' => ( $settings->has( 'client_id' ) ? $settings->get( 'client_id' ) : null ), + 'session_id' => $session_id, + 'amount' => array( + 'currency_code' => $currency->get(), + ), + 'payment_method_selected_map' => $container->get( 'axo.payment_method_selected_map' ), + 'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, + ); + }, + + // The mapping of payment methods to the PayPal Insights 'payment_method_selected' types. + 'axo.payment_method_selected_map' => static function ( ContainerInterface $container ): array { + return array( + 'ppcp-axo-gateway' => 'card', + 'ppcp-credit-card-gateway' => 'card', + 'ppcp-gateway' => 'paypal', + 'ppcp-googlepay' => 'google_pay', + 'ppcp-applepay' => 'apple_pay', + 'ppcp-multibanco' => 'other', + 'ppcp-trustly' => 'other', + 'ppcp-p24' => 'other', + 'ppcp-mybank' => 'other', + 'ppcp-ideal' => 'other', + 'ppcp-eps' => 'other', + 'ppcp-blik' => 'other', + 'ppcp-bancontact' => 'other', + 'ppcp-card-button-gateway' => 'card', + ); + }, + /** * The matrix which countries and currency combinations can be used for AXO. */ diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index d2d4351a7..6fafcb681 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -52,6 +52,13 @@ class AxoManager { */ private Environment $environment; + /** + * Data needed for the PayPal Insights. + * + * @var array + */ + private array $insights_data; + /** * The Settings status helper. * @@ -107,6 +114,7 @@ class AxoManager { * @param SessionHandler $session_handler The Session handler. * @param Settings $settings The Settings. * @param Environment $environment The environment object. + * @param array $insights_data Data needed for the PayPal Insights. * @param SettingsStatus $settings_status The Settings status helper. * @param CurrencyGetter $currency The getter of the 3-letter currency code of the shop. * @param LoggerInterface $logger The logger. @@ -120,6 +128,7 @@ class AxoManager { SessionHandler $session_handler, Settings $settings, Environment $environment, + array $insights_data, SettingsStatus $settings_status, CurrencyGetter $currency, LoggerInterface $logger, @@ -133,12 +142,13 @@ class AxoManager { $this->session_handler = $session_handler; $this->settings = $settings; $this->environment = $environment; + $this->insights_data = $insights_data; $this->settings_status = $settings_status; $this->currency = $currency; $this->logger = $logger; $this->wcgateway_module_url = $wcgateway_module_url; - $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; $this->enabled_shipping_locations = $enabled_shipping_locations; + $this->supported_country_card_type_matrix = $supported_country_card_type_matrix; } /** @@ -179,7 +189,7 @@ class AxoManager { * * @return array */ - private function script_data() { + private function script_data(): array { return array( 'environment' => array( 'is_sandbox' => $this->environment->current_environment() === 'sandbox', @@ -187,20 +197,10 @@ class AxoManager { 'widgets' => array( 'email' => 'render', ), - 'insights' => array( - 'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG, - 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), - 'session_id' => - substr( - method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '', - 0, - 16 - ), - 'amount' => array( - 'currency_code' => $this->currency->get(), - 'value' => WC()->cart->get_total( 'numeric' ), - ), - ), + // The amount is not available when setting the insights data, so we need to merge it here. + 'insights' => ( function( array $data ): array { + $data['amount']['value'] = WC()->cart->get_total( 'numeric' ); + return $data; } )( $this->insights_data ), 'allowed_cards' => $this->supported_country_card_type_matrix, 'disable_cards' => $this->settings->has( 'disable_cards' ) ? (array) $this->settings->get( 'disable_cards' ) : array(), 'enabled_shipping_locations' => $this->enabled_shipping_locations, diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 2caed32d7..b055697ba 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -320,6 +320,15 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { $endpoint->handle_request(); } ); + + // Enqueue the PayPal Insights script. + add_action( + 'wp_enqueue_scripts', + function () use ( $c ) { + $this->enqueue_paypal_insights_script_on_order_received( $c ); + } + ); + return true; } @@ -435,8 +444,8 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { * @return bool */ private function is_excluded_endpoint(): bool { - // Exclude the Order Pay endpoint. - return is_wc_endpoint_url( 'order-pay' ); + // Exclude the Order Pay and Order Received endpoints. + return is_wc_endpoint_url( 'order-pay' ) || is_wc_endpoint_url( 'order-received' ); } /** @@ -457,4 +466,57 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule { $axo_enabled ? 'enabled' : 'disabled' ); } + + /** + * Enqueues PayPal Insights on the Order Received endpoint. + * + * @param ContainerInterface $c The service container. + * @return void + */ + private function enqueue_paypal_insights_script_on_order_received( ContainerInterface $c ): void { + global $wp; + + if ( ! isset( $wp->query_vars['order-received'] ) ) { + return; + } + + $order_id = absint( $wp->query_vars['order-received'] ); + if ( ! $order_id ) { + return; + } + + $order = wc_get_order( $order_id ); + if ( ! $order || ! $order instanceof \WC_Order ) { + return; + } + + $module_url = $c->get( 'axo.url' ); + $asset_version = $c->get( 'ppcp.asset-version' ); + $insights_data = $c->get( 'axo.insights' ); + + wp_register_script( + 'wc-ppcp-paypal-insights-end-checkout', + untrailingslashit( $module_url ) . '/assets/js/TrackEndCheckout.js', + array( 'wp-plugins', 'wp-data', 'wp-element', 'wc-blocks-registry' ), + $asset_version, + true + ); + + wp_localize_script( + 'wc-ppcp-paypal-insights-end-checkout', + 'wc_ppcp_axo_insights_data', + array_merge( + $insights_data, + array( + 'orderId' => $order_id, + 'orderTotal' => (string) $order->get_total(), + 'orderCurrency' => (string) $order->get_currency(), + 'paymentMethod' => (string) $order->get_payment_method(), + 'orderKey' => (string) $order->get_order_key(), + ) + ) + ); + + wp_enqueue_script( 'wc-ppcp-paypal-insights-end-checkout' ); + } } diff --git a/modules/ppcp-axo/webpack.config.js b/modules/ppcp-axo/webpack.config.js index 95c7f0fc6..e8638b564 100644 --- a/modules/ppcp-axo/webpack.config.js +++ b/modules/ppcp-axo/webpack.config.js @@ -1,39 +1,44 @@ -const path = require('path'); +const path = require( 'path' ); const isProduction = process.env.NODE_ENV === 'production'; const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' ); module.exports = { - devtool: isProduction ? 'source-map' : 'eval-source-map', - mode: isProduction ? 'production' : 'development', - target: 'web', - plugins: [ new DependencyExtractionWebpackPlugin() ], - entry: { - 'boot': path.resolve('./resources/js/boot.js'), - 'styles': path.resolve('./resources/css/styles.scss') - }, - output: { - path: path.resolve(__dirname, 'assets/'), - filename: 'js/[name].js', - }, - module: { - rules: [{ - test: /\.js?$/, - exclude: /node_modules/, - loader: 'babel-loader', - }, - { - test: /\.scss$/, - exclude: /node_modules/, - use: [ - { - loader: 'file-loader', - options: { - name: 'css/[name].css', - } - }, - {loader:'sass-loader'} - ] - }] - } + devtool: isProduction ? 'source-map' : 'eval-source-map', + mode: isProduction ? 'production' : 'development', + target: 'web', + plugins: [ new DependencyExtractionWebpackPlugin() ], + entry: { + boot: path.resolve( './resources/js/boot.js' ), + styles: path.resolve( './resources/css/styles.scss' ), + TrackEndCheckout: path.resolve( + './resources/js/Insights/EndCheckoutTracker.js' + ), + }, + output: { + path: path.resolve( __dirname, 'assets/' ), + filename: 'js/[name].js', + }, + module: { + rules: [ + { + test: /\.js?$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.scss$/, + exclude: /node_modules/, + use: [ + { + loader: 'file-loader', + options: { + name: 'css/[name].css', + }, + }, + { loader: 'sass-loader' }, + ], + }, + ], + }, }; diff --git a/modules/ppcp-onboarding/src/OnboardingModule.php b/modules/ppcp-onboarding/src/OnboardingModule.php index 40e137832..3ec48fabf 100644 --- a/modules/ppcp-onboarding/src/OnboardingModule.php +++ b/modules/ppcp-onboarding/src/OnboardingModule.php @@ -44,26 +44,33 @@ class OnboardingModule implements ServiceModule, ExtendingModule, ExecutableModu */ public function run( ContainerInterface $c ): bool { - $asset_loader = $c->get( 'onboarding.assets' ); - /** - * The OnboardingAssets. - * - * @var OnboardingAssets $asset_loader - */ - add_action( - 'admin_enqueue_scripts', - array( - $asset_loader, - 'register', - ) - ); - add_action( - 'woocommerce_settings_checkout', - array( - $asset_loader, - 'enqueue', - ) - ); + if ( ! apply_filters( + // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores + 'woocommerce.feature-flags.woocommerce_paypal_payments.settings_enabled', + getenv( 'PCP_SETTINGS_ENABLED' ) === '1' + ) ) { + + $asset_loader = $c->get( 'onboarding.assets' ); + /** + * The OnboardingAssets. + * + * @var OnboardingAssets $asset_loader + */ + add_action( + 'admin_enqueue_scripts', + array( + $asset_loader, + 'register', + ) + ); + add_action( + 'woocommerce_settings_checkout', + array( + $asset_loader, + 'enqueue', + ) + ); + } add_filter( 'woocommerce_form_field', diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 8e75e3ad7..88df7d309 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -82,9 +82,8 @@ import { renderFields( cardFields ); } - document - .querySelector( '#place_order' ) - ?.addEventListener( 'click', ( event ) => { + const placeOrderButton = document.querySelector( '#place_order' ); + placeOrderButton?.addEventListener( 'click', ( event ) => { const cardPaymentToken = document.querySelector( 'input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked' )?.value; @@ -95,11 +94,11 @@ import { ) { return; } - + placeOrderButton.disabled = true; event.preventDefault(); - cardFields.submit().catch( ( error ) => { console.error( error ); + placeOrderButton.disabled = false; } ); } ); } ); diff --git a/modules/ppcp-settings/images/icon-dashboard-list.svg b/modules/ppcp-settings/images/icon-overview-list.svg similarity index 100% rename from modules/ppcp-settings/images/icon-dashboard-list.svg rename to modules/ppcp-settings/images/icon-overview-list.svg diff --git a/modules/ppcp-settings/images/icon-dashboard-status.svg b/modules/ppcp-settings/images/icon-overview-status.svg similarity index 100% rename from modules/ppcp-settings/images/icon-dashboard-status.svg rename to modules/ppcp-settings/images/icon-overview-status.svg diff --git a/modules/ppcp-settings/images/icon-dashboard-support.svg b/modules/ppcp-settings/images/icon-overview-support.svg similarity index 100% rename from modules/ppcp-settings/images/icon-dashboard-support.svg rename to modules/ppcp-settings/images/icon-overview-support.svg diff --git a/modules/ppcp-settings/resources/css/_variables.scss b/modules/ppcp-settings/resources/css/_variables.scss index 6d4903a96..9febb1e47 100644 --- a/modules/ppcp-settings/resources/css/_variables.scss +++ b/modules/ppcp-settings/resources/css/_variables.scss @@ -18,7 +18,7 @@ $color-gradient-dark: #001435; $gradient-header: linear-gradient(87.03deg, #003087 -0.49%, #001E51 29.22%, $color-gradient-dark 100%); $max-width-onboarding: 1024px; -$max-width-onboarding-content: 662px; +$max-width-onboarding-content: 500px; $max-width-settings: 938px; #ppcp-settings-container { 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 b73908da3..d6d8cf4f3 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 @@ -1,5 +1,5 @@ .ppcp-r-onboarding-header{ - margin: 0 0 32px 0; + margin: 0 0 24px 0; &__logo { text-align: center; 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 c32bfe706..fbfb2c7e0 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 @@ -8,14 +8,12 @@ .ppcp-r-select-box { position: relative; width: 100%; - border: 1px solid $color-gray-500; + border: 1px solid $color-gray-200; outline: 1px solid transparent; - border-radius: 8px; + border-radius: 4px; display: flex; - gap: 32px; - align-items: center; - box-sizing: border-box; - padding: 28px 16px 28px 32px; + gap: 16px; + padding: 24px 16px 24px 16px; &.selected { @@ -59,20 +57,23 @@ &__content { display: flex; - gap: 18px; } &__title { - @include font(16, 24, 600); - color: $color-blueberry; + @include font(14, 20, 700); + color: $color-black; margin: 0 0 4px 0; display: block; } &__description { - @include font(14, 20, 400); - color: $color-gray-800; - margin: 0 0 18px 0; + @include font(13, 20, 400); + color: $color-gray-700; + margin:0; + + &:not(:last-child){ + margin-block-end:18px; + } } &__radio-presentation { 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 6f067de66..7eba20d20 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 @@ -9,7 +9,7 @@ margin-left: auto; margin-right: auto; padding: 0 16px 48px; - box-sizing: border-box; + box-sizing: content-box; @media screen and (max-width: 480px) { padding-bottom: 36px; diff --git a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-products.scss b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-products.scss index 2947c8fab..2d1f759e7 100644 --- a/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-products.scss +++ b/modules/ppcp-settings/resources/css/components/screens/onboarding/_step-products.scss @@ -8,8 +8,7 @@ display: flex; align-items: center; gap: 4px; - color: $color-gray-700; - @include font(14, 20, 400); + @include font(13, 20, 400); margin: 0; &::before { @@ -29,7 +28,7 @@ .ppcp-r-select-box__additional-content { a { - @include font(12, 20, 400); + @include font(13, 20, 500); color: $color-blueberry; } } diff --git a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-dashboard.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss similarity index 98% rename from modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-dashboard.scss rename to modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss index d78d43373..bffe62134 100644 --- a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-dashboard.scss +++ b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-overview.scss @@ -1,4 +1,4 @@ -.ppcp-r-tab-dashboard-todo { +.ppcp-r-tab-overview-todo { margin: 0 0 48px 0; } diff --git a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-payment-methods.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss similarity index 100% rename from modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-payment-methods.scss rename to modules/ppcp-settings/resources/css/components/screens/overview/_tab-payment-methods.scss diff --git a/modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss b/modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss similarity index 100% rename from modules/ppcp-settings/resources/css/components/screens/dashboard/_tab-settings.scss rename to modules/ppcp-settings/resources/css/components/screens/overview/_tab-settings.scss diff --git a/modules/ppcp-settings/resources/css/style.scss b/modules/ppcp-settings/resources/css/style.scss index 5a0032536..c890803c1 100644 --- a/modules/ppcp-settings/resources/css/style.scss +++ b/modules/ppcp-settings/resources/css/style.scss @@ -20,9 +20,9 @@ @import './components/reusable-components/spinner-overlay'; @import './components/reusable-components/welcome-docs'; @import './components/screens/onboarding'; - @import './components/screens/dashboard/tab-dashboard'; - @import './components/screens/dashboard/tab-payment-methods'; - @import 'components/screens/dashboard/tab-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/SelectBox.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SelectBox.js index 06a56c5ac..145efb223 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SelectBox.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SelectBox.js @@ -31,7 +31,6 @@ const SelectBox = ( props ) => { /> ) }
- { data().getImage( props.icon ) }
{ props.title } 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 3a6632c74..6dcf435c9 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js @@ -33,22 +33,38 @@ const StepBusiness = ( {
+ + - - - -
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 f44cc89c1..d84fed57e 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js @@ -18,7 +18,7 @@ const StepProducts = ( {
@@ -27,10 +27,9 @@ const StepProducts = ( { { +const TabOverview = () => { const [ todos, setTodos ] = useState( [] ); const [ todosData, setTodosData ] = useState( todosDataDefault ); return ( -
+
{ todosData.length > 0 && ( { ) } { const tabs = []; tabs.push( { - name: 'dashboard', - title: __( 'Dashboard', 'woocommerce-paypal-payments' ), - component: , + name: 'overview', + title: __( 'Overview', 'woocommerce-paypal-payments' ), + component: , } ); tabs.push( {