From 0faeb5fd6b3041252a8c28a0afd124fed0793958 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 16:52:06 +0100 Subject: [PATCH 01/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Separate=20eligibili?= =?UTF-8?q?ty=20flags=20from=20user=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/onboarding/actions.js | 2 +- .../resources/js/data/onboarding/reducer.js | 14 ++++- .../resources/js/data/onboarding/selectors.js | 6 ++- .../src/Data/OnboardingProfile.php | 54 +++++++------------ .../src/Endpoint/OnboardingRestEndpoint.php | 41 +++++++++----- 5 files changed, 67 insertions(+), 50 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/actions.js b/modules/ppcp-settings/resources/js/data/onboarding/actions.js index 6ac070d3a..44fb37667 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/actions.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/actions.js @@ -32,7 +32,7 @@ export const setIsSaving = ( isSaving ) => { /** * Persistent. Set the full onboarding details, usually during app initialization. * - * @param {Object} payload + * @param {{data: {}, flags?: {}}} payload * @return {{type: string, payload}} The action. */ export const setOnboardingDetails = ( payload ) => { diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index e349d1f14..b932b3b9a 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -3,6 +3,8 @@ import ACTION_TYPES from './action-types'; const defaultState = { isReady: false, isSaving: false, + + // Data persisted to the server. data: { completed: false, step: 0, @@ -10,6 +12,10 @@ const defaultState = { useManualConnection: false, clientId: '', clientSecret: '', + }, + + // Read only values, provided by the server. + flags: { canUseCasualSelling: false, canUseVaulting: false, canUseCardPayments: false, @@ -49,7 +55,13 @@ export const onboardingReducer = ( // Persistent data. case ACTION_TYPES.SET_ONBOARDING_DETAILS: - return setPersistent( action.payload ); + const newState = setPersistent( action.payload.data ); + + if ( action.payload.flags ) { + newState.flags = { ...newState.flags, ...action.payload.flags }; + } + + return newState; case ACTION_TYPES.SET_ONBOARDING_COMPLETED: return setPersistent( { completed: action.completed } ); diff --git a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js index 357ef963b..b7721b992 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/selectors.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/selectors.js @@ -13,6 +13,10 @@ export const getPersistentData = ( state ) => { }; export const getTransientData = ( state ) => { - const { data, ...transientState } = getOnboardingState( state ); + const { data, flags, ...transientState } = getOnboardingState( state ); return transientState || EMPTY_OBJ; }; + +export const getFlags = ( state ) => { + return getOnboardingState( state ).flags || EMPTY_OBJ; +}; diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index 9f4f2ce2b..b0f4b5121 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -29,6 +29,13 @@ class OnboardingProfile extends AbstractDataModel { */ protected const OPTION_KEY = 'woocommerce-ppcp-data-onboarding'; + /** + * List of customization flags, provided by the server (read-only). + * + * @var array + */ + protected array $flags = array(); + /** * Constructor. * @@ -45,9 +52,9 @@ class OnboardingProfile extends AbstractDataModel { ) { parent::__construct(); - $this->data['can_use_casual_selling'] = $can_use_casual_selling; - $this->data['can_use_vaulting'] = $can_use_vaulting; - $this->data['can_use_card_payments'] = $can_use_card_payments; + $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; } /** @@ -57,15 +64,12 @@ class OnboardingProfile extends AbstractDataModel { */ protected function get_defaults() : array { return array( - 'completed' => false, - 'step' => 0, - 'use_sandbox' => false, - 'use_manual_connection' => false, - 'client_id' => '', - 'client_secret' => '', - 'can_use_casual_selling' => null, - 'can_use_vaulting' => null, - 'can_use_card_payments' => null, + 'completed' => false, + 'step' => 0, + 'use_sandbox' => false, + 'use_manual_connection' => false, + 'client_id' => '', + 'client_secret' => '', ); } @@ -180,29 +184,11 @@ class OnboardingProfile extends AbstractDataModel { } /** - * Gets whether casual selling can be used. + * Returns the list of read-only customization flags * - * @return bool + * @return array */ - public function get_can_use_casual_selling() : bool { - return (bool) $this->data['can_use_casual_selling']; - } - - /** - * Gets whether vaulting can be used. - * - * @return bool - */ - public function get_can_use_vaulting() : bool { - return (bool) $this->data['can_use_vaulting']; - } - - /** - * Gets whether Credit Card payments can be used. - * - * @return bool - */ - public function get_can_use_card_payments() : bool { - return (bool) $this->data['can_use_card_payments']; + public function get_flags() : array { + return $this->flags; } } diff --git a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php index 6e5e47bb5..bd8e57eee 100644 --- a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php @@ -41,41 +41,46 @@ class OnboardingRestEndpoint extends RestEndpoint { * @var array */ private array $field_map = array( - 'completed' => array( + 'completed' => array( 'js_name' => 'completed', 'sanitize' => 'to_boolean', ), - 'step' => array( + 'step' => array( 'js_name' => 'step', 'sanitize' => 'to_number', ), - 'use_sandbox' => array( + 'use_sandbox' => array( 'js_name' => 'useSandbox', 'sanitize' => 'to_boolean', ), - 'use_manual_connection' => array( + 'use_manual_connection' => array( 'js_name' => 'useManualConnection', 'sanitize' => 'to_boolean', ), - 'client_id' => array( + 'client_id' => array( 'js_name' => 'clientId', 'sanitize' => 'sanitize_text_field', ), - 'client_secret' => array( + 'client_secret' => array( 'js_name' => 'clientSecret', 'sanitize' => 'sanitize_text_field', ), + ); + + /** + * Map the internal flags to JS names. + * + * @var array + */ + private array $flag_map = array( 'can_use_casual_selling' => array( - 'js_name' => 'canUseCasualSelling', - 'sanitize' => 'read_only', + 'js_name' => 'canUseCasualSelling', ), 'can_use_vaulting' => array( - 'js_name' => 'canUseVaulting', - 'sanitize' => 'read_only', + 'js_name' => 'canUseVaulting', ), 'can_use_card_payments' => array( - 'js_name' => 'canUseCardPayments', - 'sanitize' => 'read_only', + 'js_name' => 'canUseCardPayments', ), ); @@ -128,7 +133,17 @@ class OnboardingRestEndpoint extends RestEndpoint { $this->field_map ); - return rest_ensure_response( $js_data ); + $js_flags = $this->sanitize_for_javascript( + $this->profile->get_flags(), + $this->flag_map + ); + + return rest_ensure_response( + array( + 'data' => $js_data, + 'flags' => $js_flags, + ) + ); } /** From 49c24f56d010f12bfad360a949475d74511faf83 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 16:55:44 +0100 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Add?= =?UTF-8?q?=20a=20Reset-Store=20action=20for=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/onboarding/action-types.js | 2 ++ .../resources/js/data/onboarding/actions.js | 9 +++++++++ .../resources/js/data/onboarding/reducer.js | 4 ++++ modules/ppcp-settings/resources/js/data/store.js | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/action-types.js b/modules/ppcp-settings/resources/js/data/onboarding/action-types.js index ac5a6a0df..b5f634ea5 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/action-types.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/action-types.js @@ -1,4 +1,6 @@ export default { + RESET_ONBOARDING: 'RESET_ONBOARDING', + // Transient data. SET_ONBOARDING_IS_READY: 'SET_ONBOARDING_IS_READY', SET_IS_SAVING_ONBOARDING: 'SET_IS_SAVING_ONBOARDING', diff --git a/modules/ppcp-settings/resources/js/data/onboarding/actions.js b/modules/ppcp-settings/resources/js/data/onboarding/actions.js index 44fb37667..635e72da6 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/actions.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/actions.js @@ -3,6 +3,15 @@ import { apiFetch } from '@wordpress/data-controls'; import ACTION_TYPES from './action-types'; import { NAMESPACE, STORE_NAME } from '../constants'; +/** + * Special. Resets all values in the onboarding store to initial defaults. + * + * @return {{type: string}} The action. + */ +export const resetOnboarding = () => { + return { type: ACTION_TYPES.RESET_ONBOARDING }; +}; + /** * Non-persistent. Marks the onboarding details as "ready", i.e., fully initialized. * diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index b932b3b9a..47c0b034d 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -46,6 +46,10 @@ export const onboardingReducer = ( }; switch ( type ) { + // Reset store to initial state. + case ACTION_TYPES.RESET_ONBOARDING: + return setPersistent( defaultState.data ); + // Transient data. case ACTION_TYPES.SET_ONBOARDING_IS_READY: return setTransient( { isReady: action.isReady } ); diff --git a/modules/ppcp-settings/resources/js/data/store.js b/modules/ppcp-settings/resources/js/data/store.js index 32a05411f..a4acaf548 100644 --- a/modules/ppcp-settings/resources/js/data/store.js +++ b/modules/ppcp-settings/resources/js/data/store.js @@ -44,9 +44,14 @@ export const initStore = () => { console.groupEnd(); }; + window.ppcpSettings.resetStore = () => { + wp.data.dispatch( STORE_NAME ).resetOnboarding(); + wp.data.dispatch( STORE_NAME ).persist(); + }; window.ppcpSettings.startOnboarding = () => { wp.data.dispatch( STORE_NAME ).setCompleted( false ); wp.data.dispatch( STORE_NAME ).setOnboardingStep( 0 ); + wp.data.dispatch( STORE_NAME ).persist(); }; } /* eslint-enable no-console */ From 6a30bb01b3c962716fed45aa8ab8bd37cb0253be Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 16:56:42 +0100 Subject: [PATCH 03/15] =?UTF-8?q?=F0=9F=91=94=20Introduce=20new=20props=20?= =?UTF-8?q?in=20the=20Redux=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/data/onboarding/action-types.js | 2 ++ .../resources/js/data/onboarding/actions.js | 26 +++++++++++++++++++ .../resources/js/data/onboarding/reducer.js | 8 ++++++ 3 files changed, 36 insertions(+) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/action-types.js b/modules/ppcp-settings/resources/js/data/onboarding/action-types.js index b5f634ea5..5d719f417 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/action-types.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/action-types.js @@ -13,4 +13,6 @@ export default { SET_MANUAL_CONNECTION_MODE: 'SET_MANUAL_CONNECTION_MODE', SET_CLIENT_ID: 'SET_CLIENT_ID', SET_CLIENT_SECRET: 'SET_CLIENT_SECRET', + SET_IS_CASUAL_SELLER: 'SET_IS_CASUAL_SELLER', + SET_PRODUCTS: 'SET_PRODUCTS', }; diff --git a/modules/ppcp-settings/resources/js/data/onboarding/actions.js b/modules/ppcp-settings/resources/js/data/onboarding/actions.js index 635e72da6..1409c471a 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/actions.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/actions.js @@ -129,6 +129,32 @@ export const setClientSecret = ( clientSecret ) => { }; }; +/** + * Persistent. Sets the "isCasualSeller" value. + * + * @param {boolean} isCasualSeller + * @return {{type: string, isCasualSeller}} The action. + */ +export const setIsCasualSeller = ( isCasualSeller ) => { + return { + type: ACTION_TYPES.SET_IS_CASUAL_SELLER, + isCasualSeller, + }; +}; + +/** + * Persistent. Sets the "products" array. + * + * @param {string[]} products + * @return {{type: string, products}} The action. + */ +export const setProducts = ( products ) => { + return { + type: ACTION_TYPES.SET_PRODUCTS, + products, + }; +}; + /** * Saves the persistent details to the WP database. * diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js index 47c0b034d..c0a47cb5e 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js @@ -12,6 +12,8 @@ const defaultState = { useManualConnection: false, clientId: '', clientSecret: '', + isCasualSeller: null, // null value will uncheck both options in the UI. + products: [], }, // Read only values, provided by the server. @@ -87,6 +89,12 @@ export const onboardingReducer = ( useManualConnection: action.useManualConnection, } ); + case ACTION_TYPES.SET_IS_CASUAL_SELLER: + return setPersistent( { isCasualSeller: action.isCasualSeller } ); + + case ACTION_TYPES.SET_PRODUCTS: + return setPersistent( { products: action.products } ); + default: return state; } From ee2c374f790908e1362032d13b45c9c051bdaa43 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 17:02:01 +0100 Subject: [PATCH 04/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20constants=20t?= =?UTF-8?q?o=20a=20central=20config=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason: The constants are also used by the store and are synced to the server. Their use is not isolated to the specific component --- .../Components/Screens/Onboarding/StepBusiness.js | 10 +++++----- .../Components/Screens/Onboarding/StepProducts.js | 13 ++++++------- .../ppcp-settings/resources/js/data/constants.js | 11 +++++++++++ 3 files changed, 22 insertions(+), 12 deletions(-) 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 9ac7de1c2..fe7deeb90 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js @@ -5,6 +5,9 @@ import { __ } from '@wordpress/i18n'; import PaymentMethodIcons from '../../ReusableComponents/PaymentMethodIcons'; import { useState } from '@wordpress/element'; import Navigation from '../../ReusableComponents/Navigation'; +import { BUSINESS_TYPES } from '../../../data/constants'; + +const BUSINESS_RADIO_GROUP_NAME = 'business'; const StepBusiness = ( { setStep, @@ -13,9 +16,6 @@ const StepBusiness = ( { setCompleted, } ) => { const [ businessCategory, setBusinessCategory ] = useState( null ); - const BUSINESS_RADIO_GROUP_NAME = 'business'; - const CASUAL_SELLER_CHECKBOX_VALUE = 'casual_seller'; - const BUSINESS_CHECKBOX_VALUE = 'business'; return (
@@ -38,7 +38,7 @@ const StepBusiness = ( { ) } icon="icon-business-casual-seller.svg" name={ BUSINESS_RADIO_GROUP_NAME } - value={ CASUAL_SELLER_CHECKBOX_VALUE } + value={ BUSINESS_TYPES.CASUAL_SELLER } changeCallback={ setBusinessCategory } currentValue={ businessCategory } checked={ @@ -69,7 +69,7 @@ const StepBusiness = ( { ) } icon="icon-business-business.svg" name={ BUSINESS_RADIO_GROUP_NAME } - value={ BUSINESS_CHECKBOX_VALUE } + value={ BUSINESS_TYPES.BUSINESS } currentValue={ businessCategory } changeCallback={ setBusinessCategory } checked={ 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 efc477d2b..c2ce87012 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js @@ -4,6 +4,9 @@ import { __ } from '@wordpress/i18n'; import SelectBox from '../../ReusableComponents/SelectBox'; import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; import { useState } from '@wordpress/element'; +import { PRODUCT_TYPES } from '../../../data/constants'; + +const PRODUCTS_CHECKBOX_GROUP_NAME = 'products'; const StepProducts = ( { setStep, @@ -12,10 +15,6 @@ const StepProducts = ( { setCompleted, } ) => { const [ products, setProducts ] = useState( [] ); - const PRODUCTS_CHECKBOX_GROUP_NAME = 'products'; - const VIRTUAL_CHECKBOX_VALUE = 'virtual'; - const PHYSICAL_CHECKBOX_VALUE = 'physical'; - const SUBSCRIPTIONS_CHECKBOX_VALUE = 'subscriptions'; return (
@@ -35,7 +34,7 @@ const StepProducts = ( { ) } icon="icon-product-virtual.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } - value={ VIRTUAL_CHECKBOX_VALUE } + value={ PRODUCT_TYPES.VIRTUAL } changeCallback={ setProducts } currentValue={ products } type="checkbox" @@ -78,7 +77,7 @@ const StepProducts = ( { ) } icon="icon-product-physical.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } - value={ PHYSICAL_CHECKBOX_VALUE } + value={ PRODUCT_TYPES.PHYSICAL } changeCallback={ setProducts } currentValue={ products } type="checkbox" @@ -106,7 +105,7 @@ const StepProducts = ( { ) } icon="icon-product-subscription.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } - value={ SUBSCRIPTIONS_CHECKBOX_VALUE } + value={ PRODUCT_TYPES.SUBSCRIPTIONS } changeCallback={ setProducts } currentValue={ products } type="checkbox" diff --git a/modules/ppcp-settings/resources/js/data/constants.js b/modules/ppcp-settings/resources/js/data/constants.js index 61ebf948e..e6f8f9de5 100644 --- a/modules/ppcp-settings/resources/js/data/constants.js +++ b/modules/ppcp-settings/resources/js/data/constants.js @@ -1,2 +1,13 @@ export const NAMESPACE = '/wc/v3/wc_paypal'; export const STORE_NAME = 'wc/paypal'; + +export const BUSINESS_TYPES = { + CASUAL_SELLER: 'casual_seller', + BUSINESS: 'business', +}; + +export const PRODUCT_TYPES = { + VIRTUAL: 'virtual', + PHYSICAL: 'physical', + SUBSCRIPTIONS: 'subscriptions', +}; From fcf2a972a71b4f3f694a18158e079f174c6cbebb Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 17:17:36 +0100 Subject: [PATCH 05/15] =?UTF-8?q?=F0=9F=91=94=20Introduce=20new=20hook=20d?= =?UTF-8?q?etails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/onboarding/hooks.js | 24 ++++++++++++++++++- 1 file changed, 23 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 66bfec24a..5db54f95c 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -1,5 +1,5 @@ import { useSelect, useDispatch } from '@wordpress/data'; -import { STORE_NAME } from '../constants'; +import { PRODUCT_TYPES, STORE_NAME } from '../constants'; export const useOnboardingDetails = () => { const { @@ -8,6 +8,8 @@ export const useOnboardingDetails = () => { setManualConnectionMode, setClientId, setClientSecret, + setIsCasualSeller, + setProducts, } = useDispatch( STORE_NAME ); // Transient accessors. @@ -32,6 +34,21 @@ export const useOnboardingDetails = () => { return select( STORE_NAME ).getPersistentData().useManualConnection; }, [] ); + const isCasualSeller = useSelect( ( select ) => { + return select( STORE_NAME ).getPersistentData().isCasualSeller; + }, [] ); + + const products = useSelect( ( select ) => { + return select( STORE_NAME ).getPersistentData().products || []; + }, [] ); + + const toggleProduct = ( list ) => { + const validProducts = list.filter( ( item ) => + Object.values( PRODUCT_TYPES ).includes( item ) + ); + return setDetailAndPersist( setProducts, validProducts ); + }; + const setDetailAndPersist = async ( setter, value ) => { setter( value ); await persist(); @@ -50,6 +67,11 @@ export const useOnboardingDetails = () => { setDetailAndPersist( setSandboxMode, state ), setManualConnectionMode: ( state ) => setDetailAndPersist( setManualConnectionMode, state ), + isCasualSeller, + setIsCasualSeller: ( value ) => + setDetailAndPersist( setIsCasualSeller, value ), + products, + toggleProduct, }; }; From cff9919c7810a891785eb6be8db68cdbf1280945 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 17:19:47 +0100 Subject: [PATCH 06/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Consolidate=20hook?= =?UTF-8?q?=20props?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/data/onboarding/hooks.js | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js index 5db54f95c..b5f478afb 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -4,6 +4,8 @@ import { PRODUCT_TYPES, STORE_NAME } from '../constants'; export const useOnboardingDetails = () => { const { persist, + setOnboardingStep, + setCompleted, setSandboxMode, setManualConnectionMode, setClientId, @@ -17,7 +19,19 @@ export const useOnboardingDetails = () => { return select( STORE_NAME ).getTransientData().isSaving; }, [] ); + const isReady = useSelect( ( select ) => { + return select( STORE_NAME ).getTransientData().isReady; + } ); + // Persistent accessors. + const step = useSelect( ( select ) => { + return select( STORE_NAME ).getPersistentData().step || 0; + } ); + + const completed = useSelect( ( select ) => { + return select( STORE_NAME ).getPersistentData().completed; + } ); + const clientId = useSelect( ( select ) => { return select( STORE_NAME ).getPersistentData().clientId; }, [] ); @@ -56,17 +70,22 @@ export const useOnboardingDetails = () => { return { isSaving, + isReady, + step, + setStep: ( value ) => setDetailAndPersist( setOnboardingStep, value ), + completed, + setCompleted: ( state ) => setDetailAndPersist( setCompleted, state ), isSandboxMode, + setSandboxMode: ( state ) => + setDetailAndPersist( setSandboxMode, state ), isManualConnectionMode, + setManualConnectionMode: ( state ) => + setDetailAndPersist( setManualConnectionMode, state ), clientId, setClientId: ( value ) => setDetailAndPersist( setClientId, value ), clientSecret, setClientSecret: ( value ) => setDetailAndPersist( setClientSecret, value ), - setSandboxMode: ( state ) => - setDetailAndPersist( setSandboxMode, state ), - setManualConnectionMode: ( state ) => - setDetailAndPersist( setManualConnectionMode, state ), isCasualSeller, setIsCasualSeller: ( value ) => setDetailAndPersist( setIsCasualSeller, value ), @@ -76,31 +95,8 @@ export const useOnboardingDetails = () => { }; export const useOnboardingStep = () => { - const { persist, setOnboardingStep, setCompleted } = - useDispatch( STORE_NAME ); + const { isReady, step, setStep, completed, setCompleted } = + useOnboardingDetails(); - const isReady = useSelect( ( select ) => { - return select( STORE_NAME ).getTransientData().isReady; - } ); - - const step = useSelect( ( select ) => { - return select( STORE_NAME ).getPersistentData().step || 0; - } ); - - const completed = useSelect( ( select ) => { - return select( STORE_NAME ).getPersistentData().completed; - } ); - - const setDetailAndPersist = async ( setter, value ) => { - setter( value ); - await persist(); - }; - - return { - isReady, - step, - setStep: ( value ) => setDetailAndPersist( setOnboardingStep, value ), - completed, - setCompleted: ( state ) => setDetailAndPersist( setCompleted, state ), - }; + return { isReady, step, setStep, completed, setCompleted }; }; From 6e3664ade80771ae0c4af0484dbd1c2f5cccbf02 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 17:33:48 +0100 Subject: [PATCH 07/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20onboarding?= =?UTF-8?q?=20state=20to=20custom=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/StepBusiness.js | 37 ++++++++++------- .../Screens/Onboarding/StepProducts.js | 10 ++--- .../Screens/Onboarding/StepWelcome.js | 4 +- .../resources/js/data/onboarding/hooks.js | 40 ++++++++++++++++++- 4 files changed, 69 insertions(+), 22 deletions(-) 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 fe7deeb90..1c38ecc86 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepBusiness.js @@ -3,7 +3,7 @@ import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper.js'; import SelectBox from '../../ReusableComponents/SelectBox.js'; import { __ } from '@wordpress/i18n'; import PaymentMethodIcons from '../../ReusableComponents/PaymentMethodIcons'; -import { useState } from '@wordpress/element'; +import { useOnboardingStepBusiness } from '../../../data'; import Navigation from '../../ReusableComponents/Navigation'; import { BUSINESS_TYPES } from '../../../data/constants'; @@ -15,7 +15,21 @@ const StepBusiness = ( { stepperOrder, setCompleted, } ) => { - const [ businessCategory, setBusinessCategory ] = useState( null ); + const { isCasualSeller, setIsCasualSeller } = useOnboardingStepBusiness(); + + const handleSellerTypeChange = ( value ) => { + setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === value ); + }; + + const getCurrentValue = () => { + if ( isCasualSeller === null ) { + return ''; + } + + return isCasualSeller + ? BUSINESS_TYPES.CASUAL_SELLER + : BUSINESS_TYPES.BUSINESS; + }; return (
@@ -39,12 +53,9 @@ const StepBusiness = ( { icon="icon-business-casual-seller.svg" name={ BUSINESS_RADIO_GROUP_NAME } value={ BUSINESS_TYPES.CASUAL_SELLER } - changeCallback={ setBusinessCategory } - currentValue={ businessCategory } - checked={ - businessCategory === - { CASUAL_SELLER_CHECKBOX_VALUE } - } + changeCallback={ handleSellerTypeChange } + currentValue={ getCurrentValue() } + checked={ isCasualSeller === true } type="radio" > businessCategory !== null } + canProceeedCallback={ () => isCasualSeller !== null } />
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 c2ce87012..fecdc6029 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepProducts.js @@ -3,7 +3,7 @@ import Navigation from '../../ReusableComponents/Navigation'; import { __ } from '@wordpress/i18n'; import SelectBox from '../../ReusableComponents/SelectBox'; import SelectBoxWrapper from '../../ReusableComponents/SelectBoxWrapper'; -import { useState } from '@wordpress/element'; +import { useOnboardingStepProducts } from '../../../data'; import { PRODUCT_TYPES } from '../../../data/constants'; const PRODUCTS_CHECKBOX_GROUP_NAME = 'products'; @@ -14,7 +14,7 @@ const StepProducts = ( { stepperOrder, setCompleted, } ) => { - const [ products, setProducts ] = useState( [] ); + const { products, toggleProduct } = useOnboardingStepProducts(); return (
@@ -35,7 +35,7 @@ const StepProducts = ( { icon="icon-product-virtual.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } value={ PRODUCT_TYPES.VIRTUAL } - changeCallback={ setProducts } + changeCallback={ toggleProduct } currentValue={ products } type="checkbox" > @@ -78,7 +78,7 @@ const StepProducts = ( { icon="icon-product-physical.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } value={ PRODUCT_TYPES.PHYSICAL } - changeCallback={ setProducts } + changeCallback={ toggleProduct } currentValue={ products } type="checkbox" > @@ -106,7 +106,7 @@ const StepProducts = ( { icon="icon-product-subscription.svg" name={ PRODUCTS_CHECKBOX_GROUP_NAME } value={ PRODUCT_TYPES.SUBSCRIPTIONS } - changeCallback={ setProducts } + changeCallback={ toggleProduct } currentValue={ products } type="checkbox" > 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 b6a5bb097..368b4d9c9 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/StepWelcome.js @@ -4,7 +4,7 @@ import { Button, TextControl } from '@wordpress/components'; import PaymentMethodIcons from '../../ReusableComponents/PaymentMethodIcons'; import SettingsToggleBlock from '../../ReusableComponents/SettingsToggleBlock'; import Separator from '../../ReusableComponents/Separator'; -import { useOnboardingDetails } from '../../../data'; +import { useOnboardingStepWelcome } from '../../../data'; import DataStoreControl from '../../ReusableComponents/DataStoreControl'; const StepWelcome = ( { setStep, currentStep } ) => { @@ -84,7 +84,7 @@ const WelcomeForm = () => { setClientId, clientSecret, setClientSecret, - } = useOnboardingDetails(); + } = useOnboardingStepWelcome(); const advancedUsersDescription = sprintf( // translators: %s: Link to PayPal REST application guide diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js index b5f478afb..9481e047d 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -1,7 +1,7 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { PRODUCT_TYPES, STORE_NAME } from '../constants'; -export const useOnboardingDetails = () => { +const useOnboardingDetails = () => { const { persist, setOnboardingStep, @@ -94,6 +94,44 @@ export const useOnboardingDetails = () => { }; }; +export const useOnboardingStepWelcome = () => { + const { + isSaving, + isSandboxMode, + setSandboxMode, + isManualConnectionMode, + setManualConnectionMode, + clientId, + setClientId, + clientSecret, + setClientSecret, + } = useOnboardingDetails(); + + return { + isSaving, + isSandboxMode, + setSandboxMode, + isManualConnectionMode, + setManualConnectionMode, + clientId, + setClientId, + clientSecret, + setClientSecret, + }; +}; + +export const useOnboardingStepBusiness = () => { + const { isCasualSeller, setIsCasualSeller } = useOnboardingDetails(); + + return { isCasualSeller, setIsCasualSeller }; +}; + +export const useOnboardingStepProducts = () => { + const { products, toggleProduct } = useOnboardingDetails(); + + return { products, toggleProduct }; +}; + export const useOnboardingStep = () => { const { isReady, step, setStep, completed, setCompleted } = useOnboardingDetails(); From e28b6e35aa2b73a7bcc04f5d31dd42a3729b30bd Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 18:52:10 +0100 Subject: [PATCH 08/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20boolean=20g?= =?UTF-8?q?etters/setters=20for=20data=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make the function names more semantic --- .../src/Data/AbstractDataModel.php | 32 +++++++++++++++++-- .../src/Data/OnboardingProfile.php | 8 ++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-settings/src/Data/AbstractDataModel.php b/modules/ppcp-settings/src/Data/AbstractDataModel.php index 1d711cbe5..780ad40bd 100644 --- a/modules/ppcp-settings/src/Data/AbstractDataModel.php +++ b/modules/ppcp-settings/src/Data/AbstractDataModel.php @@ -89,10 +89,38 @@ abstract class AbstractDataModel { continue; } - $setter = "set_$key"; - if ( method_exists( $this, $setter ) ) { + $setter = $this->get_setter_name( $key ); + + if ( $setter && method_exists( $this, $setter ) ) { $this->$setter( $value ); } } } + + /** + * Generates a setter method name for a given key, stripping the prefix from + * boolean fields (is_, use_, has_). + * + * @param int|string $field_key The key for which to generate a setter name. + * + * @return string The generated setter method name. + */ + private function get_setter_name( $field_key ) : string { + if ( ! is_string( $field_key ) ) { + return ''; + } + + $prefixes_to_strip = array( 'is_', 'use_', 'has_' ); + $stripped_key = $field_key; + + foreach ( $prefixes_to_strip as $prefix ) { + if ( str_starts_with( $field_key, $prefix ) ) { + $stripped_key = substr( $field_key, strlen( $prefix ) ); + break; + } + } + + return $stripped_key ? "set_$stripped_key" : ''; + } + } diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index b0f4b5121..df894e85a 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -116,7 +116,7 @@ class OnboardingProfile extends AbstractDataModel { * * @return bool */ - public function get_use_sandbox() : bool { + public function get_sandbox() : bool { return (bool) $this->data['use_sandbox']; } @@ -125,7 +125,7 @@ class OnboardingProfile extends AbstractDataModel { * * @param bool $use_sandbox Whether to use sandbox mode. */ - public function set_use_sandbox( bool $use_sandbox ) : void { + public function set_sandbox( bool $use_sandbox ) : void { $this->data['use_sandbox'] = $use_sandbox; } @@ -134,7 +134,7 @@ class OnboardingProfile extends AbstractDataModel { * * @return bool */ - public function get_use_manual_connection() : bool { + public function get_manual_connection() : bool { return (bool) $this->data['use_manual_connection']; } @@ -143,7 +143,7 @@ class OnboardingProfile extends AbstractDataModel { * * @param bool $use_manual_connection Whether to use manual connection. */ - public function set_use_manual_connection( bool $use_manual_connection ) : void { + public function set_manual_connection( bool $use_manual_connection ) : void { $this->data['use_manual_connection'] = $use_manual_connection; } From 4102ba285b601dc2eb5a24b1521452360eab2f39 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 18:55:07 +0100 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=91=94=20Add=20server-side=20onboar?= =?UTF-8?q?ding=20profile=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Data/OnboardingProfile.php | 38 +++++++++++++++++++ .../src/Endpoint/OnboardingRestEndpoint.php | 7 ++++ 2 files changed, 45 insertions(+) diff --git a/modules/ppcp-settings/src/Data/OnboardingProfile.php b/modules/ppcp-settings/src/Data/OnboardingProfile.php index df894e85a..e1f9e16b4 100644 --- a/modules/ppcp-settings/src/Data/OnboardingProfile.php +++ b/modules/ppcp-settings/src/Data/OnboardingProfile.php @@ -70,6 +70,8 @@ class OnboardingProfile extends AbstractDataModel { 'use_manual_connection' => false, 'client_id' => '', 'client_secret' => '', + 'is_casual_seller' => null, + 'products' => array(), ); } @@ -183,6 +185,42 @@ class OnboardingProfile extends AbstractDataModel { $this->data['client_secret'] = sanitize_text_field( $client_secret ); } + /** + * Gets the casual seller flag. + * + * @return bool|null + */ + public function get_casual_seller() : ?bool { + return $this->data['is_casual_seller']; + } + + /** + * Sets the casual-seller flag. + * + * @param bool|null $casual_seller Whether the merchant uses a personal account for selling. + */ + public function set_casual_seller( ?bool $casual_seller ) : void { + $this->data['is_casual_seller'] = $casual_seller; + } + + /** + * Gets the active product types for this store. + * + * @return string[] + */ + public function get_products() : array { + return $this->data['products']; + } + + /** + * Sets the list of active product types. + * + * @param string[] $products Any of ['virtual'|'physical'|'subscriptions']. + */ + public function set_products( array $products ) : void { + $this->data['products'] = $products; + } + /** * Returns the list of read-only customization flags * diff --git a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php index bd8e57eee..afb2d7f1f 100644 --- a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php @@ -65,6 +65,13 @@ class OnboardingRestEndpoint extends RestEndpoint { 'js_name' => 'clientSecret', 'sanitize' => 'sanitize_text_field', ), + 'is_casual_seller' => array( + 'js_name' => 'isCasualSeller', + 'sanitize' => 'to_boolean', + ), + 'products' => array( + 'js_name' => 'products', + ), ); /** From 37beb591d5b17365a120b25eb9e2e8074554d948 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 30 Oct 2024 18:57:51 +0100 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20Add=20array=20san?= =?UTF-8?q?itizer=20for=20products=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php | 2 ++ modules/ppcp-settings/src/Endpoint/RestEndpoint.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php index afb2d7f1f..6c59b1622 100644 --- a/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php @@ -98,6 +98,8 @@ class OnboardingRestEndpoint extends RestEndpoint { */ public function __construct( OnboardingProfile $profile ) { $this->profile = $profile; + + $this->field_map['products']['sanitize'] = fn( $list ) => array_map( 'sanitize_text_field', $list ); } /** diff --git a/modules/ppcp-settings/src/Endpoint/RestEndpoint.php b/modules/ppcp-settings/src/Endpoint/RestEndpoint.php index 9bb98dfac..08191276b 100644 --- a/modules/ppcp-settings/src/Endpoint/RestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/RestEndpoint.php @@ -65,7 +65,7 @@ class RestEndpoint extends WC_REST_Controller { if ( null === $sanitation_cb ) { $sanitized[ $key ] = $value; - } elseif ( method_exists( $this, $sanitation_cb ) ) { + } elseif ( is_string( $sanitation_cb ) && method_exists( $this, $sanitation_cb ) ) { $sanitized[ $key ] = $this->{$sanitation_cb}( $value ); } elseif ( is_callable( $sanitation_cb ) ) { $sanitized[ $key ] = $sanitation_cb( $value ); From 847bce3510afee90a87e9c77c01095ddebee7a55 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 31 Oct 2024 14:44:45 +0100 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=8E=A8=20Standardize=20imports,=20r?= =?UTF-8?q?emove=20unneccesary=20consts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/ReusableComponents/Container.js | 3 --- .../js/Components/Screens/Onboarding/Onboarding.js | 12 +++++------- .../js/Components/Screens/Onboarding/StepBusiness.js | 6 +++--- .../js/Components/Screens/Onboarding/StepWelcome.js | 2 +- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Container.js b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Container.js index e6b83e691..fd91017bf 100644 --- a/modules/ppcp-settings/resources/js/Components/ReusableComponents/Container.js +++ b/modules/ppcp-settings/resources/js/Components/ReusableComponents/Container.js @@ -1,6 +1,3 @@ -export const PAGE_ONBOARDING = 'onboarding'; -export const PAGE_SETTINGS = 'settings'; - const Container = ( { isCard = true, page, children } ) => { let className = 'ppcp-r-container'; 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 195e3a089..712b17f86 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -1,16 +1,14 @@ -import Container, { - PAGE_ONBOARDING, -} from '../../ReusableComponents/Container.js'; -import StepWelcome from './StepWelcome.js'; -import StepBusiness from './StepBusiness.js'; -import StepProducts from './StepProducts.js'; +import Container from '../../ReusableComponents/Container'; +import StepWelcome from './StepWelcome'; +import StepBusiness from './StepBusiness'; +import StepProducts from './StepProducts'; import { useOnboardingStep } from '../../../data'; const Onboarding = () => { const { step, setStep, setCompleted } = useOnboardingStep(); return ( - +
Date: Thu, 31 Oct 2024 15:03:25 +0100 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Add=20conditional?= =?UTF-8?q?=20routing=20to=20onboarding=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/Components/Screens/Onboarding/Onboarding.js | 11 +++++------ .../Screens/Onboarding/availableSteps.js | 14 ++++++++++++++ .../resources/js/data/onboarding/hooks.js | 11 +++++++++-- 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js 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 712b17f86..212776a0a 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -1,11 +1,9 @@ import Container from '../../ReusableComponents/Container'; -import StepWelcome from './StepWelcome'; -import StepBusiness from './StepBusiness'; -import StepProducts from './StepProducts'; import { useOnboardingStep } from '../../../data'; +import { getSteps } from './availableSteps'; const Onboarding = () => { - const { step, setStep, setCompleted } = useOnboardingStep(); + const { step, setStep, setCompleted, flags } = useOnboardingStep(); return ( @@ -14,14 +12,15 @@ const Onboarding = () => { currentStep={ step } setStep={ setStep } setCompleted={ setCompleted } + flags={ flags } />
); }; -const OnboardingStep = ( { currentStep, setStep, setCompleted } ) => { - const stepperOrder = [ StepWelcome, StepBusiness, StepProducts ]; +const OnboardingStep = ( { currentStep, setStep, setCompleted, flags } ) => { + const stepperOrder = getSteps( flags ); const isValidStep = ( step ) => typeof step === 'number' && diff --git a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js new file mode 100644 index 000000000..dbb25f57e --- /dev/null +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js @@ -0,0 +1,14 @@ +import StepWelcome from './StepWelcome'; +import StepBusiness from './StepBusiness'; +import StepProducts from './StepProducts'; + +export const getSteps = ( flags ) => { + console.log( 'Step Flags:', flags ); + const allSteps = [ StepWelcome, StepBusiness, StepProducts ]; + + if ( ! flags.canUseCasualSelling ) { + return allSteps.filter( ( step ) => step !== StepBusiness ); + } + + return allSteps; +}; diff --git a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js index 9481e047d..e21cc5016 100644 --- a/modules/ppcp-settings/resources/js/data/onboarding/hooks.js +++ b/modules/ppcp-settings/resources/js/data/onboarding/hooks.js @@ -1,5 +1,6 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { PRODUCT_TYPES, STORE_NAME } from '../constants'; +import { getFlags } from './selectors'; const useOnboardingDetails = () => { const { @@ -23,6 +24,11 @@ const useOnboardingDetails = () => { return select( STORE_NAME ).getTransientData().isReady; } ); + // Read-only flags. + const flags = useSelect( ( select ) => { + return select( STORE_NAME ).getFlags(); + } ); + // Persistent accessors. const step = useSelect( ( select ) => { return select( STORE_NAME ).getPersistentData().step || 0; @@ -91,6 +97,7 @@ const useOnboardingDetails = () => { setDetailAndPersist( setIsCasualSeller, value ), products, toggleProduct, + flags, }; }; @@ -133,8 +140,8 @@ export const useOnboardingStepProducts = () => { }; export const useOnboardingStep = () => { - const { isReady, step, setStep, completed, setCompleted } = + const { isReady, step, setStep, completed, setCompleted, flags } = useOnboardingDetails(); - return { isReady, step, setStep, completed, setCompleted }; + return { isReady, step, setStep, completed, setCompleted, flags }; }; From 05657b8b27d7996c7cc179ef9c54ff0eae42e070 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 31 Oct 2024 15:11:36 +0100 Subject: [PATCH 13/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20Onboardin?= =?UTF-8?q?g=20component=20and=20step=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screens/Onboarding/Onboarding.js | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 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 212776a0a..20c18fc33 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Onboarding.js @@ -2,44 +2,35 @@ import Container from '../../ReusableComponents/Container'; import { useOnboardingStep } from '../../../data'; import { getSteps } from './availableSteps'; +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 } = useOnboardingStep(); + const steps = getSteps( flags ); + + const CurrentStepComponent = getCurrentStep( step, steps ); return (
-
); }; -const OnboardingStep = ( { currentStep, setStep, setCompleted, flags } ) => { - const stepperOrder = getSteps( flags ); - - const isValidStep = ( step ) => - typeof step === 'number' && - Number.isInteger( step ) && - step >= 0 && - step < stepperOrder.length; - - const safeCurrentStep = isValidStep( currentStep ) ? currentStep : 0; - - const CurrentStepComponent = stepperOrder[ safeCurrentStep ]; - - return ( - - ); -}; - export default Onboarding; From 654b2e40e99456686be869ba7929642542e95121 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 31 Oct 2024 16:17:15 +0100 Subject: [PATCH 14/15] =?UTF-8?q?=E2=9C=A8=20Add=20casual=20selling=20elig?= =?UTF-8?q?ibility=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ppcp-settings/services.php | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index f34257f79..3620957f9 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -14,7 +14,7 @@ use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint; use WooCommerce\PayPalCommerce\Settings\Data\OnboardingProfile; return array( - 'settings.url' => static function ( ContainerInterface $container ) : string { + 'settings.url' => static function ( ContainerInterface $container ) : string { /** * The path cannot be false. * @@ -25,12 +25,13 @@ return array( dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php' ); }, - 'settings.data.onboarding' => static function ( ContainerInterface $container ) : OnboardingProfile { - $can_use_casual_selling = false; + 'settings.data.onboarding' => static function ( ContainerInterface $container ) : OnboardingProfile { + $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' ); // Card payments are disabled for this plugin when WooPayments is active. + // TODO: Move this condition to the card-fields.eligible service? if ( class_exists( '\WC_Payments' ) ) { $can_use_card_payments = false; } @@ -41,7 +42,26 @@ return array( $can_use_card_payments ); }, - 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { + 'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint { return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); }, + 'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array { + // TODO: This is a dummy list, while we wait for the official eligibility list. + + return array( + 'US', + 'CA', + 'DE', + 'ES', + 'AT', + 'CH', + 'NL', + ); + }, + 'settings.casual-selling.eligible' => static function ( ContainerInterface $container ) : bool { + $country = $container->get( 'api.shop.country' ); + $eligible_countries = $container->get( 'settings.casual-selling.supported-countries' ); + + return in_array( $country, $eligible_countries, true ); + }, ); From a881838cf44eca7ec2848513c6519afec98572b2 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Thu, 31 Oct 2024 16:22:07 +0100 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=94=87=20Remove=20console.log=20fro?= =?UTF-8?q?m=20getSteps=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/js/Components/Screens/Onboarding/availableSteps.js | 1 - 1 file changed, 1 deletion(-) 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 dbb25f57e..32034ec57 100644 --- a/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js +++ b/modules/ppcp-settings/resources/js/Components/Screens/Onboarding/availableSteps.js @@ -3,7 +3,6 @@ import StepBusiness from './StepBusiness'; import StepProducts from './StepProducts'; export const getSteps = ( flags ) => { - console.log( 'Step Flags:', flags ); const allSteps = [ StepWelcome, StepBusiness, StepProducts ]; if ( ! flags.canUseCasualSelling ) {