diff --git a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss index df0cde379..1efe54236 100644 --- a/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss +++ b/modules/ppcp-settings/resources/css/components/reusable-components/_payment-method-modal.scss @@ -119,8 +119,6 @@ background-color: $color-white; &::before { - transform: translate(3px, 3px); - border-width: 6px; border-color: $color-blueberry; } } 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 0d104a70c..bcc20c5ef 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/FeatureSettingsBlock.js @@ -19,6 +19,27 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => { ); }; + const renderButton = ( button ) => { + const buttonElement = ( + + ); + + return button.urls ? ( + + { buttonElement } + + ) : ( + buttonElement + ); + }; + return (
@@ -35,16 +56,7 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
- { props.actionProps?.buttons.map( ( button ) => ( - - ) ) } + { props.actionProps?.buttons.map( renderButton ) }
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 cdad46b3e..c2a7c5a70 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodItemBlock.js @@ -1,51 +1,50 @@ -import { useState } from '@wordpress/element'; import { ToggleControl } from '@wordpress/components'; import SettingsBlock from './SettingsBlock'; import PaymentMethodIcon from '../PaymentMethodIcon'; import data from '../../../utils/data'; +import { MODAL_CONFIG } from '../../Screens/Overview/Modals/Modal'; -const PaymentMethodItemBlock = ( props ) => { - const [ toggleIsChecked, setToggleIsChecked ] = useState( false ); - const [ modalIsVisible, setModalIsVisible ] = useState( false ); - const Modal = props?.modal; +const PaymentMethodItemBlock = ( { + id, + title, + description, + icon, + onTriggerModal, + onSelect, + isSelected, +} ) => { + // Only show settings icon if this method has a modal configured + const hasModal = Boolean( MODAL_CONFIG[ id ] ); return ( - <> - -
-
- - - { props.title } - -
-

- { props.description } -

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

+ { description } +

+
+ + { hasModal && onTriggerModal && ( +
+ { data().getImage( 'icon-settings.svg' ) } +
+ ) } +
+
+
); }; 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 3ffb91051..1caae6ce6 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsBlocks/PaymentMethodsBlock.js @@ -2,7 +2,11 @@ import { useState, useCallback } from '@wordpress/element'; import SettingsBlock from './SettingsBlock'; import PaymentMethodItemBlock from './PaymentMethodItemBlock'; -const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => { +const PaymentMethodsBlock = ( { + paymentMethods, + className = '', + onTriggerModal, +} ) => { const [ selectedMethod, setSelectedMethod ] = useState( null ); const handleSelect = useCallback( ( methodId, isSelected ) => { @@ -25,6 +29,9 @@ const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => { onSelect={ ( checked ) => handleSelect( paymentMethod.id, checked ) } + onTriggerModal={ () => + onTriggerModal?.( paymentMethod.id ) + } /> ) ) } diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js index aeb0e3561..b7b6b8de4 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/SettingsCard.js @@ -1,6 +1,7 @@ import { Content, ContentWrapper } from './SettingsBlocks'; const SettingsCard = ( { + id, className: extraClassName, title, description, @@ -33,7 +34,7 @@ const SettingsCard = ( { }; return ( -
+
diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js index e22711255..e6561f56f 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/TabNavigation.js @@ -1,4 +1,6 @@ import { useCallback, useEffect, useState } from '@wordpress/element'; + +// TODO: Migrate to Tabs (TabPanel v2) once its API is publicly available, as it provides programmatic tab switching support: https://github.com/WordPress/gutenberg/issues/52997 import { TabPanel } from '@wordpress/components'; import { getQuery, updateQueryString } from '../../utils/navigation'; diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js new file mode 100644 index 000000000..a0c401d44 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/Modals/Modal.js @@ -0,0 +1,51 @@ +import { __ } from '@wordpress/i18n'; +import ModalPayPal from './ModalPayPal'; +import ModalFastlane from './ModalFastlane'; +import ModalAcdc from './ModalAcdc'; +import { useActiveModal } from '../../../../data/common/hooks'; + +export const MODAL_CONFIG = { + paypal: { + component: ModalPayPal, + icon: 'payment-method-paypal-big', + title: __( 'PayPal', 'woocommerce-paypal-payments' ), + }, + fastlane: { + component: ModalFastlane, + icon: 'payment-method-fastlane-big', + title: __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ), + size: 'small', + }, + advanced_credit_and_debit_card_payments: { + component: ModalAcdc, + icon: 'payment-method-cards-big', + title: __( + 'Advanced Credit and Debit Card Payments', + 'woocommerce-paypal-payments' + ), + }, +}; + +const Modal = () => { + const { activeModal, setActiveModal } = useActiveModal(); + + const handleCloseModal = () => { + setActiveModal( '' ); + }; + + if ( ! activeModal || ! MODAL_CONFIG[ activeModal ] ) { + return null; + } + + const { component: ModalComponent, ...modalProps } = + MODAL_CONFIG[ activeModal ]; + + return ( + + ); +}; + +export default Modal; 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 f64095a8f..25e713bcd 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabOverview.js @@ -1,5 +1,5 @@ import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; +import { useState, useMemo } from '@wordpress/element'; import { Button, Icon } from '@wordpress/components'; import { useDispatch } from '@wordpress/data'; import { reusableBlock } from '@wordpress/icons'; @@ -10,6 +10,7 @@ import FeatureSettingsBlock from '../../ReusableComponents/SettingsBlocks/Featur import { TITLE_BADGE_POSITIVE } from '../../ReusableComponents/TitleBadge'; import { useMerchantInfo } from '../../../data/common/hooks'; import { STORE_NAME } from '../../../data/common'; +import Features from './TabSettingsElements/Blocks/Features'; const TabOverview = () => { const [ todos, setTodos ] = useState( [] ); @@ -17,32 +18,39 @@ const TabOverview = () => { const [ isRefreshing, setIsRefreshing ] = useState( false ); const { merchantFeatures } = useMerchantInfo(); - const { refreshFeatureStatuses } = useDispatch( STORE_NAME ); + const { refreshFeatureStatuses, setActiveModal } = + useDispatch( STORE_NAME ); - const features = featuresDefault.map( ( feature ) => { - const merchantFeature = merchantFeatures?.[ feature.id ]; - return { - ...feature, - enabled: merchantFeature?.enabled ?? false, - }; - } ); + // Get the features data with access to setActiveModal + const featuresData = useMemo( + () => Features.getFeatures( setActiveModal ), + [ setActiveModal ] + ); + + // Map merchant features status to our config + const features = useMemo( () => { + return featuresData.map( ( feature ) => { + const merchantFeature = merchantFeatures?.[ feature.id ]; + return { + ...feature, + enabled: merchantFeature?.enabled ?? false, + }; + } ); + }, [ featuresData, merchantFeatures ] ); const refreshHandler = async () => { setIsRefreshing( true ); - - 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:', - result.message || 'Unknown error' - ); - } else { - console.log( 'Features refreshed successfully.' ); + try { + const result = await refreshFeatureStatuses(); + if ( result && ! result.success ) { + console.error( + 'Failed to refresh features:', + result.message || 'Unknown error' + ); + } + } finally { + setIsRefreshing( false ); } - - setIsRefreshing( false ); }; return ( @@ -103,27 +111,46 @@ const TabOverview = () => { } - contentItems={ features.map( ( feature ) => ( - - ) ) } + contentItems={ features.map( ( feature ) => { + return ( + + ! button.showWhen || // Learn more buttons + ( feature.enabled && + button.showWhen === + 'enabled' ) || + ( ! feature.enabled && + button.showWhen === 'disabled' ) + ) + .map( ( button ) => ( { + ...button, + url: button.urls + ? merchant?.isSandbox + ? button.urls.sandbox + : button.urls.live + : button.url, + } ) ), + enabled: feature.enabled, + notes: feature.notes, + badge: feature.enabled + ? { + text: __( + 'Active', + 'woocommerce-paypal-payments' + ), + type: TITLE_BADGE_POSITIVE, + } + : undefined, + } } + /> + ); + } ) } /> { const { storeCountry, storeCurrency } = CommonHooks.useWooSettings(); + const { setActiveModal } = useActiveModal(); const filteredPaymentMethods = useMemo( () => { const contextProps = { storeCountry, storeCurrency }; @@ -33,6 +33,7 @@ const TabPaymentMethods = () => { return (
{ > { > { > + +
); }; @@ -98,7 +106,6 @@ const paymentMethodsPayPalCheckout = [ 'woocommerce-paypal-payments' ), icon: 'payment-method-paypal', - modal: ModalPayPal, }, { id: 'venmo', @@ -144,7 +151,6 @@ const paymentMethodsOnlineCardPayments = [ 'woocommerce-paypal-payments' ), icon: 'payment-method-advanced-cards', - modal: ModalAcdc, }, { id: 'fastlane', @@ -154,7 +160,6 @@ const paymentMethodsOnlineCardPayments = [ 'woocommerce-paypal-payments' ), icon: 'payment-method-fastlane', - modal: ModalFastlane, }, { id: 'apply_pay', diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Features.js b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Features.js new file mode 100644 index 000000000..0529cba66 --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Overview/TabSettingsElements/Blocks/Features.js @@ -0,0 +1,297 @@ +import { __ } from '@wordpress/i18n'; +import { TAB_IDS, selectTab } from '../../../../../utils/tabSelector'; + +const Features = { + getFeatures: ( setActiveModal ) => [ + { + id: 'save_paypal_and_venmo', + title: __( 'Save PayPal and Venmo', 'woocommerce-paypal-payments' ), + description: __( + 'Securely save PayPal and Venmo payment methods for subscriptions or return buyers.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-paypal-checkout-card' + ).then( () => { + setActiveModal( 'paypal' ); + } ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: + 'https://www.sandbox.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING', + live: 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + }, + { + id: 'advanced_credit_and_debit_cards', + title: __( + 'Advanced Credit and Debit Cards', + 'woocommerce-paypal-payments' + ), + description: __( + 'Process major credit and debit cards including Visa, Mastercard, American Express and Discover.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-card-payments-card' + ).then( () => { + setActiveModal( + 'advanced_credit_and_debit_card_payments' + ); + } ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: + 'https://www.sandbox.paypal.com/bizsignup/entry?product=ppcp', + live: 'https://www.paypal.com/bizsignup/entry?product=ppcp', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + }, + { + id: 'alternative_payment_methods', + title: __( + 'Alternative Payment Methods', + 'woocommerce-paypal-payments' + ), + description: __( + 'Offer global, country-specific payment options for your customers.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-alternative-payments-card' + ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + }, + { + id: 'google_pay', + title: __( 'Google Pay', 'woocommerce-paypal-payments' ), + description: __( + 'Let customers pay using their Google Pay wallet.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-card-payments-card' + ).then( () => { + setActiveModal( 'google_pay' ); + } ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: + 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY', + live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + notes: [ + __( + '¹PayPal Q2 Earnings-2021.', + 'woocommerce-paypal-payments' + ), + ], + }, + { + id: 'apple_pay', + title: __( 'Apple Pay', 'woocommerce-paypal-payments' ), + description: __( + 'Let customers pay using their Apple Pay wallet.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-card-payments-card' + ).then( () => { + setActiveModal( 'apple_pay' ); + } ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( + 'Domain registration', + 'woocommerce-paypal-payments' + ), + urls: { + sandbox: + 'https://www.sandbox.paypal.com/uccservicing/apm/applepay', + live: 'https://www.paypal.com/uccservicing/apm/applepay', + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: + 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY', + live: 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=APPLE_PAY', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + }, + { + id: 'pay_later_messaging', + title: __( 'Pay Later Messaging', 'woocommerce-paypal-payments' ), + description: __( + 'Let customers know they can buy now and pay later with PayPal. Adding this messaging can boost conversion rates and increase cart sizes by 39%¹, with no extra cost to you—plus, you get paid up front.', + 'woocommerce-paypal-payments' + ), + buttons: [ + { + type: 'secondary', + text: __( 'Configure', 'woocommerce-paypal-payments' ), + onClick: () => { + selectTab( + TAB_IDS.PAYMENT_METHODS, + 'ppcp-paypal-checkout-card' + ).then( () => { + setActiveModal( 'paypal' ); + } ); + }, + showWhen: 'enabled', + class: 'small-button', + }, + { + type: 'secondary', + text: __( 'Apply', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + showWhen: 'disabled', + class: 'small-button', + }, + { + type: 'tertiary', + text: __( 'Learn more', 'woocommerce-paypal-payments' ), + urls: { + sandbox: '#', + live: '#', + }, + class: 'small-button', + }, + ], + }, + ], +}; + +export default Features; diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js index 0cdec5d31..c859fe0be 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions.js +++ b/modules/ppcp-settings/resources/js/data/common/actions.js @@ -47,6 +47,17 @@ export const setIsReady = ( isReady ) => ( { payload: { isReady }, } ); +/** + * Transient. Sets the active settings tab. + * + * @param {string} activeModal + * @return {Action} The action. + */ +export const setActiveModal = ( activeModal ) => ( { + type: ACTION_TYPES.SET_TRANSIENT, + payload: { activeModal }, +} ); + /** * Transient. Changes the "saving" flag. * diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js index e9a77b79b..4aea08caa 100644 --- a/modules/ppcp-settings/resources/js/data/common/hooks.js +++ b/modules/ppcp-settings/resources/js/data/common/hooks.js @@ -32,12 +32,14 @@ const useHooks = () => { productionOnboardingUrl, authenticateWithCredentials, authenticateWithOAuth, + setActiveModal, startWebhookSimulation, checkWebhookSimulationState, } = useDispatch( STORE_NAME ); // Transient accessors. const isReady = useTransient( 'isReady' ); + const activeModal = useTransient( 'activeModal' ); // Persistent accessors. const isSandboxMode = usePersistent( 'useSandbox' ); @@ -68,6 +70,8 @@ const useHooks = () => { return { isReady, + activeModal, + setActiveModal, isSandboxMode, setSandboxMode: ( state ) => { return savePersistent( setSandboxMode, state ); @@ -161,6 +165,11 @@ export const useMerchantInfo = () => { }; }; +export const useActiveModal = () => { + const { activeModal, setActiveModal } = useHooks(); + return { activeModal, setActiveModal }; +}; + // -- Not using the `useHooks()` data provider -- export const useBusyState = () => { diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js index 019d29911..e9ff90a42 100644 --- a/modules/ppcp-settings/resources/js/data/common/reducer.js +++ b/modules/ppcp-settings/resources/js/data/common/reducer.js @@ -15,6 +15,7 @@ import ACTION_TYPES from './action-types'; const defaultTransient = Object.freeze( { isReady: false, activities: new Map(), + activeModal: '', // Read only values, provided by the server via hydrate. merchant: Object.freeze( { diff --git a/modules/ppcp-settings/resources/js/utils/tabSelector.js b/modules/ppcp-settings/resources/js/utils/tabSelector.js new file mode 100644 index 000000000..c8b1c5350 --- /dev/null +++ b/modules/ppcp-settings/resources/js/utils/tabSelector.js @@ -0,0 +1,59 @@ +// Tab panel IDs +export const TAB_IDS = { + OVERVIEW: 'tab-panel-0-overview', + PAYMENT_METHODS: 'tab-panel-0-payment-methods', + SETTINGS: 'tab-panel-0-settings', + STYLING: 'tab-panel-0-styling', +}; + +/** + * Select a tab by simulating a click event and scroll to specified element, + * accounting for navigation container height + * + * TODO: Once the TabPanel gets migrated to Tabs (TabPanel v2) we need to remove this in favor of programmatic tab switching: https://github.com/WordPress/gutenberg/issues/52997 + * + * @param {string} tabId - The ID of the tab to select + * @param {string} [scrollToId] - Optional ID of the element to scroll to + * @return {Promise} - Resolves when tab switch and scroll are complete + */ +export const selectTab = ( tabId, scrollToId ) => { + return new Promise( ( resolve ) => { + const tab = document.getElementById( tabId ); + if ( tab ) { + tab.click(); + setTimeout( () => { + const scrollTarget = scrollToId + ? document.getElementById( scrollToId ) + : document.getElementById( 'ppcp-settings-container' ); + + if ( scrollTarget ) { + const navContainer = document.querySelector( + '.ppcp-r-navigation-container' + ); + const navHeight = navContainer + ? navContainer.offsetHeight + : 0; + + // Get the current scroll position and element's position relative to viewport + const rect = scrollTarget.getBoundingClientRect(); + + // Calculate the final position with offset + const scrollPosition = + rect.top + window.scrollY - ( navHeight + 55 ); + + window.scrollTo( { + top: scrollPosition, + behavior: 'smooth', + } ); + + // Resolve after scroll animation + setTimeout( resolve, 300 ); + } else { + resolve(); + } + }, 100 ); + } else { + resolve(); + } + } ); +}; diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index 6f249006a..761743817 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -187,6 +187,40 @@ class CommonRestEndpoint extends RestEndpoint { return $this->return_success( $js_data, $extra_data ); } + /** + * Returns mocked merchant data until real data fetching is implemented. + * + * @return array Mocked merchant details including connection status, features, etc. + */ + protected function get_mocked_merchant_data(): array { + return array( + 'isConnected' => true, + 'isSandbox' => false, + 'id' => '', + 'email' => '', + 'features' => array( + 'save_paypal_and_venmo' => array( + 'enabled' => true, + ), + 'advanced_credit_and_debit_cards' => array( + 'enabled' => true, + ), + 'alternative_payment_methods' => array( + 'enabled' => true, + ), + 'google_pay' => array( + 'enabled' => true, + ), + 'apple_pay' => array( + 'enabled' => true, + ), + 'pay_later_messaging' => array( + 'enabled' => true, + ), + ), + ); + } + /** * Appends the "merchant" attribute to the extra_data collection, which * contains details about the merchant's PayPal account, like the merchant ID. @@ -195,7 +229,7 @@ class CommonRestEndpoint extends RestEndpoint { * * @return array Updated extra_data collection. */ - protected function add_merchant_info( array $extra_data ) : array { + protected function add_merchant_info( array $extra_data ): array { $extra_data['merchant'] = $this->sanitize_for_javascript( $this->settings->to_array(), $this->merchant_info_map @@ -208,6 +242,11 @@ class CommonRestEndpoint extends RestEndpoint { ); } + // If no real data is available yet, use mock data. + if ( empty( $extra_data['merchant'] ) || ( empty( $extra_data['merchant']['id'] ) && empty( $extra_data['merchant']['email'] ) ) ) { + $extra_data['merchant'] = $this->get_mocked_merchant_data(); + } + return $extra_data; }