From f99117fd1ca8e586ad08586463cee4e77a6d23fd Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Tue, 27 Aug 2024 16:25:49 +0530 Subject: [PATCH 1/7] Limits steps in onboarding based on previously completed state --- js/src/setup-mc/setup-stepper/index.js | 41 +++++++++++++++--- .../setup-stepper/saved-setup-stepper.js | 43 ++++++++++++------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/js/src/setup-mc/setup-stepper/index.js b/js/src/setup-mc/setup-stepper/index.js index 2c35a06064..81cd851f6c 100644 --- a/js/src/setup-mc/setup-stepper/index.js +++ b/js/src/setup-mc/setup-stepper/index.js @@ -9,14 +9,28 @@ import { getHistory, getNewPath } from '@woocommerce/navigation'; import AppSpinner from '.~/components/app-spinner'; import SavedSetupStepper from './saved-setup-stepper'; import useMCSetup from '.~/hooks/useMCSetup'; +import useStoreAddress from '.~/hooks/useStoreAddress'; +import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; import stepNameKeyMap from './stepNameKeyMap'; const SetupStepper = () => { const { hasFinishedResolution, data: mcSetup } = useMCSetup(); + const { data: address, loaded: addressLoaded } = useStoreAddress(); + const { data: phone, loaded: phoneLoaded } = useGoogleMCPhoneNumber(); - if ( ! hasFinishedResolution && ! mcSetup ) { - return ; - } + const hasValidPhoneNumber = + phoneLoaded && phone?.isValid && phone?.isVerified; + + const hasValidAddress = + addressLoaded && + address?.isAddressFilled && + ! address?.isMCAddressDifferent; + + const hasConfirmedStoreRequirements = + hasValidPhoneNumber && hasValidAddress; + + const hasLoaded = + hasFinishedResolution && mcSetup && addressLoaded && phoneLoaded; if ( hasFinishedResolution && ! mcSetup ) { // this means error occurred, we just need to return null here, @@ -24,14 +38,31 @@ const SetupStepper = () => { return null; } - const { status, step } = mcSetup; + if ( ! hasLoaded ) { + return ; + } + + const { status } = mcSetup; + let { step } = mcSetup; + + // If the user has already completed the store requirements, but is currently still on the + // store requirements step, we should skip the store requirements step and go to the paid ads step. + // else they will get stuck on a non-existent step #3 + if ( step === 'store_requirements' && hasConfirmedStoreRequirements ) { + step = 'paid_ads'; + } if ( status === 'complete' ) { getHistory().replace( getNewPath( {}, '/google/dashboard' ) ); return null; } - return ; + return ( + + ); }; export default SetupStepper; diff --git a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js index 000b0a9336..cda9a40a91 100644 --- a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js +++ b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js @@ -36,7 +36,10 @@ import { * @fires gla_setup_mc with `{ triggered_by: 'step1-continue-button' | 'step2-continue-button', 'step3-continue-button', action: 'go-to-step2' | 'go-to-step3' | 'go-to-step4' }`. * @fires gla_setup_mc with `{ triggered_by: 'stepper-step1-button' | 'stepper-step2-button' | 'stepper-step3-button', action: 'go-to-step1' | 'go-to-step2' | 'go-to-step3' }`. */ -const SavedSetupStepper = ( { savedStep } ) => { +const SavedSetupStepper = ( { + savedStep, + hasConfirmedStoreRequirements = false, +} ) => { const [ step, setStep ] = useState( savedStep ); const { settings } = useSettings(); @@ -102,7 +105,11 @@ const SavedSetupStepper = ( { savedStep } ) => { }; const handleSetupListingsContinue = () => { - continueStep( stepNameKeyMap.store_requirements ); + if ( hasConfirmedStoreRequirements ) { + continueStep( stepNameKeyMap.paid_ads ); + } else { + continueStep( stepNameKeyMap.store_requirements ); + } }; const handleStoreRequirementsContinue = () => { @@ -209,19 +216,25 @@ const SavedSetupStepper = ( { savedStep } ) => { ), onClick: handleStepClick, }, - { - key: stepNameKeyMap.store_requirements, - label: __( - 'Confirm store requirements', - 'google-listings-and-ads' - ), - content: ( - - ), - onClick: handleStepClick, - }, + ...( ! hasConfirmedStoreRequirements + ? [ + { + key: stepNameKeyMap.store_requirements, + label: __( + 'Confirm store requirements', + 'google-listings-and-ads' + ), + content: ( + + ), + onClick: handleStepClick, + }, + ] + : [] ), { key: stepNameKeyMap.paid_ads, label: __( 'Create a campaign', 'google-listings-and-ads' ), From d6f3a76abd69311ff0b8d35f22ad11d2a7408fb0 Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Tue, 27 Aug 2024 16:40:37 +0530 Subject: [PATCH 2/7] Fixes linter warnings --- js/src/setup-mc/setup-stepper/saved-setup-stepper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js index cda9a40a91..4484bcb8d9 100644 --- a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js +++ b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js @@ -33,6 +33,7 @@ import { /** * @param {Object} props React props * @param {string} [props.savedStep] A saved step overriding the current step + * @param {boolean} [props.hasConfirmedStoreRequirements] Whether the store requirements have been confirmed * @fires gla_setup_mc with `{ triggered_by: 'step1-continue-button' | 'step2-continue-button', 'step3-continue-button', action: 'go-to-step2' | 'go-to-step3' | 'go-to-step4' }`. * @fires gla_setup_mc with `{ triggered_by: 'stepper-step1-button' | 'stepper-step2-button' | 'stepper-step3-button', action: 'go-to-step1' | 'go-to-step2' | 'go-to-step3' }`. */ From 67ad90d5b65e8b3988baef8ae8ff81cd8e7fdcc6 Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Wed, 28 Aug 2024 10:17:42 +0530 Subject: [PATCH 3/7] Updates step conditional per feedback --- js/src/setup-mc/setup-stepper/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/js/src/setup-mc/setup-stepper/index.js b/js/src/setup-mc/setup-stepper/index.js index 81cd851f6c..2c8e7560c2 100644 --- a/js/src/setup-mc/setup-stepper/index.js +++ b/js/src/setup-mc/setup-stepper/index.js @@ -12,6 +12,7 @@ import useMCSetup from '.~/hooks/useMCSetup'; import useStoreAddress from '.~/hooks/useStoreAddress'; import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; import stepNameKeyMap from './stepNameKeyMap'; +import { has } from 'lodash'; const SetupStepper = () => { const { hasFinishedResolution, data: mcSetup } = useMCSetup(); @@ -43,14 +44,15 @@ const SetupStepper = () => { } const { status } = mcSetup; - let { step } = mcSetup; + const { step } = mcSetup; // If the user has already completed the store requirements, but is currently still on the // store requirements step, we should skip the store requirements step and go to the paid ads step. // else they will get stuck on a non-existent step #3 - if ( step === 'store_requirements' && hasConfirmedStoreRequirements ) { - step = 'paid_ads'; - } + const currentStep = + step === 'store_requirements' && hasConfirmedStoreRequirements + ? 'paid_ads' + : step; if ( status === 'complete' ) { getHistory().replace( getNewPath( {}, '/google/dashboard' ) ); @@ -59,7 +61,7 @@ const SetupStepper = () => { return ( ); From 20ed5c7e0a683633e150dc4ab684a17c5d1a53e3 Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Wed, 28 Aug 2024 11:45:47 +0530 Subject: [PATCH 4/7] First pass at the hide store requirements e2e tests --- .../step-3-hide-store-requirements.test.js | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js diff --git a/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js b/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js new file mode 100644 index 0000000000..51dbbc5183 --- /dev/null +++ b/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js @@ -0,0 +1,81 @@ +/** + * Internal dependencies + */ +import SetUpAccountsPage from '../../utils/pages/setup-mc/step-1-set-up-accounts'; + +/** + * External dependencies + */ +const { test, expect } = require( '@playwright/test' ); + +test.use( { storageState: process.env.ADMINSTATE } ); + +test.describe.configure( { mode: 'serial' } ); + +/** + * @type {import('../../utils/pages/setup-mc/step-1-set-up-accounts.js').default} setUpAccountsPage + */ +let setUpAccountsPage = null; + +/** + * @type {import('@playwright/test').Page} page + */ +let page = null; + +test.describe( 'Hide Store Requirements Step', () => { + test.beforeAll( async ( { browser } ) => { + page = await browser.newPage(); + setUpAccountsPage = new SetUpAccountsPage( page ); + await Promise.all( [ + // Mock google as connected. + setUpAccountsPage.mockGoogleNotConnected(), + + // Mock MC contact information + setUpAccountsPage.mockContactInformation(), + ] ); + } ); + + test.afterAll( async () => { + await setUpAccountsPage.closePage(); + } ); + + test( 'should have store requirements step if incomplete', async () => { + await setUpAccountsPage.goto(); + + // Mock MC step at step 1: + setUpAccountsPage.mockMCSetup( 'incomplete', 'accounts' ); + + // 1. Assert there are 3 steps + const steps = await page.locator( '.woocommerce-stepper__step' ); + await expect( steps ).toHaveCount( 4 ); + + // 2. Assert the label of the 3rd step is 'Confirm store requirements' + const thirdStepLabel = await steps + .nth( 2 ) + .locator( '.woocommerce-stepper__step-label' ); + await expect( thirdStepLabel ).toHaveText( + 'Confirm store requirements' + ); + } ); + + test( 'should not have store requirements step if complete', async () => { + await setUpAccountsPage.goto(); + + // TODO: Mock email is verified & address is filled + + // 1. Assert there are 3 steps + const steps = await page.locator( '.woocommerce-stepper__step' ); + await expect( steps ).toHaveCount( 3 ); + + // 2. Assert the label of the 3rd step is not 'Confirm store requirements' + const thirdStepLabel = await steps + .nth( 2 ) + .locator( '.woocommerce-stepper__step-label' ); + await expect( thirdStepLabel ).not.toHaveText( + 'Confirm store requirements' + ); + + // 3. Assert the label of the 3rd step equals 'Create a campaign' + await expect( thirdStepLabel ).toHaveText( 'Create a campaign' ); + } ); +} ); From 31a3ba5017434a3d4f6995f89839dd64546eb888 Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Wed, 28 Aug 2024 11:50:05 +0530 Subject: [PATCH 5/7] Fixes linter warning --- js/src/setup-mc/setup-stepper/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/src/setup-mc/setup-stepper/index.js b/js/src/setup-mc/setup-stepper/index.js index 2c8e7560c2..3e4954994e 100644 --- a/js/src/setup-mc/setup-stepper/index.js +++ b/js/src/setup-mc/setup-stepper/index.js @@ -12,7 +12,6 @@ import useMCSetup from '.~/hooks/useMCSetup'; import useStoreAddress from '.~/hooks/useStoreAddress'; import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; import stepNameKeyMap from './stepNameKeyMap'; -import { has } from 'lodash'; const SetupStepper = () => { const { hasFinishedResolution, data: mcSetup } = useMCSetup(); From abc86c3a2ce2a6813c6da38d7707b6b70ff1934f Mon Sep 17 00:00:00 2001 From: Darshan Sawardekar Date: Wed, 28 Aug 2024 11:55:40 +0530 Subject: [PATCH 6/7] Switches to complete step mock --- tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js b/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js index 51dbbc5183..c9f29447e6 100644 --- a/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js +++ b/tests/e2e/specs/setup-mc/step-3-hide-store-requirements.test.js @@ -62,6 +62,7 @@ test.describe( 'Hide Store Requirements Step', () => { await setUpAccountsPage.goto(); // TODO: Mock email is verified & address is filled + setUpAccountsPage.mockMCSetup( 'complete', 'accounts' ); // 1. Assert there are 3 steps const steps = await page.locator( '.woocommerce-stepper__step' ); From b3d873037bd843dbe68a618f628ad97f3c4aac7e Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 23 Sep 2024 20:31:20 -0500 Subject: [PATCH 7/7] Check store requirements in saved setup stepper. This updates both the `useStoreAddress` and `useGoogleMCPhoneNumber` hooks to check for a connected MC account before fetching contact information so the saved setup stepper can determine if the user has the necessary information to proceed prior to connecting an MC account. --- js/src/hooks/useGoogleMCPhoneNumber.js | 62 ++++++---- js/src/hooks/useStoreAddress.js | 110 +++++++++++------- js/src/setup-mc/setup-stepper/index.js | 42 +------ .../setup-stepper/saved-setup-stepper.js | 54 +++++---- 4 files changed, 144 insertions(+), 124 deletions(-) diff --git a/js/src/hooks/useGoogleMCPhoneNumber.js b/js/src/hooks/useGoogleMCPhoneNumber.js index 1be1a8ce26..cfad17973f 100644 --- a/js/src/hooks/useGoogleMCPhoneNumber.js +++ b/js/src/hooks/useGoogleMCPhoneNumber.js @@ -8,6 +8,7 @@ import { parsePhoneNumberFromString as parsePhoneNumber } from 'libphonenumber-j * Internal dependencies */ import { STORE_KEY } from '.~/data/constants'; +import useGoogleMCAccount from './useGoogleMCAccount'; const emptyData = { country: '', @@ -40,29 +41,48 @@ const emptyData = { * @return {PhoneNumber} The payload of parsed phone number associated with the Google Merchant Center account and its loaded state. */ export default function useGoogleMCPhoneNumber() { - return useSelect( ( select ) => { - const { getGoogleMCPhoneNumber } = select( STORE_KEY ); - const { data: contact, loaded } = getGoogleMCPhoneNumber(); - let data = emptyData; + const { googleMCAccount } = useGoogleMCAccount(); - if ( contact ) { - // Prevent to call parsePhoneNumber with null. - const parsed = parsePhoneNumber( contact.phone_number || '' ); - if ( parsed ) { - data = { - ...parsed, - isValid: parsed.isValid(), - isVerified: - contact.phone_verification_status === 'verified', - display: parsed.formatInternational(), + return useSelect( + ( select ) => { + let data = emptyData; + + // If there is no MC account then there is no phone number data to fetch. + if ( ! googleMCAccount?.id ) { + return { + loaded: false, + data, }; - delete data.metadata; } - } - return { - loaded, - data, - }; - }, [] ); + const { getGoogleMCContactInformation, hasFinishedResolution } = + select( STORE_KEY ); + + const contact = getGoogleMCContactInformation(); + const loaded = hasFinishedResolution( + 'getGoogleMCContactInformation' + ); + + if ( contact && loaded ) { + // Prevent to call parsePhoneNumber with null. + const parsed = parsePhoneNumber( contact.phone_number || '' ); + if ( parsed ) { + data = { + ...parsed, + isValid: parsed.isValid(), + isVerified: + contact.phone_verification_status === 'verified', + display: parsed.formatInternational(), + }; + delete data.metadata; + } + } + + return { + loaded, + data, + }; + }, + [ googleMCAccount?.id ] + ); } diff --git a/js/src/hooks/useStoreAddress.js b/js/src/hooks/useStoreAddress.js index 8a332770ba..4d99469661 100644 --- a/js/src/hooks/useStoreAddress.js +++ b/js/src/hooks/useStoreAddress.js @@ -1,8 +1,14 @@ +/** + * External dependencies + */ +import { useSelect } from '@wordpress/data'; + /** * Internal dependencies */ -import useAppSelectDispatch from './useAppSelectDispatch'; +import { STORE_KEY } from '.~/data/constants'; import useCountryKeyNameMap from './useCountryKeyNameMap'; +import useGoogleMCAccount from './useGoogleMCAccount'; /** * @typedef {import('.~/hooks/types.js').StoreAddress} StoreAddress @@ -36,53 +42,73 @@ const emptyData = { * @return {StoreAddressResult} Store address result. */ export default function useStoreAddress( source = 'wc' ) { - const { - data: contact, - hasFinishedResolution: loaded, - invalidateResolution: refetch, - } = useAppSelectDispatch( 'getGoogleMCContactInformation' ); - + const { googleMCAccount } = useGoogleMCAccount(); const countryNameDict = useCountryKeyNameMap(); - let data = emptyData; + return useSelect( + ( select ) => { + let data = emptyData; + + // If there is no MC account then there is no store address data to fetch. + if ( ! googleMCAccount?.id ) { + return { + refetch: () => {}, + loaded: false, + data, + }; + } + + const { + getGoogleMCContactInformation, + hasFinishedResolution, + refetch, + } = select( STORE_KEY ); + + const contact = getGoogleMCContactInformation(); + const loaded = hasFinishedResolution( + 'getGoogleMCContactInformation' + ); - if ( loaded && contact ) { - const { - is_mc_address_different: isMCAddressDifferent, - wc_address_errors: missingRequiredFields, - } = contact; + if ( contact && loaded ) { + const { + is_mc_address_different: isMCAddressDifferent, + wc_address_errors: missingRequiredFields, + } = contact; - const storeAddress = - source === 'wc' ? contact.wc_address : contact.mc_address; + const storeAddress = + source === 'wc' ? contact.wc_address : contact.mc_address; - // Handle fallback for `null` fields to make sure the returned data types are consistent. - const streetAddress = storeAddress?.street_address || ''; - const city = storeAddress?.locality || ''; - const state = storeAddress?.region || ''; - const postcode = storeAddress?.postal_code || ''; + // Handle fallback for `null` fields to make sure the returned data types are consistent. + const streetAddress = storeAddress?.street_address || ''; + const city = storeAddress?.locality || ''; + const state = storeAddress?.region || ''; + const postcode = storeAddress?.postal_code || ''; - const [ address, address2 = '' ] = streetAddress.split( '\n' ); - const country = countryNameDict[ storeAddress?.country ] || ''; - const countryCode = storeAddress?.country || ''; - const isAddressFilled = ! missingRequiredFields.length; + const [ address, address2 = '' ] = streetAddress.split( '\n' ); + const country = countryNameDict[ storeAddress?.country ] || ''; + const countryCode = storeAddress?.country || ''; + const isAddressFilled = ! missingRequiredFields.length; - data = { - countryCode, - address, - address2, - city, - state, - country, - postcode, - isAddressFilled, - isMCAddressDifferent, - missingRequiredFields, - }; - } + data = { + countryCode, + address, + address2, + city, + state, + country, + postcode, + isAddressFilled, + isMCAddressDifferent, + missingRequiredFields, + }; + } - return { - refetch, - loaded, - data, - }; + return { + refetch, + loaded, + data, + }; + }, + [ countryNameDict, googleMCAccount?.id, source ] + ); } diff --git a/js/src/setup-mc/setup-stepper/index.js b/js/src/setup-mc/setup-stepper/index.js index 3e4954994e..2c35a06064 100644 --- a/js/src/setup-mc/setup-stepper/index.js +++ b/js/src/setup-mc/setup-stepper/index.js @@ -9,28 +9,14 @@ import { getHistory, getNewPath } from '@woocommerce/navigation'; import AppSpinner from '.~/components/app-spinner'; import SavedSetupStepper from './saved-setup-stepper'; import useMCSetup from '.~/hooks/useMCSetup'; -import useStoreAddress from '.~/hooks/useStoreAddress'; -import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; import stepNameKeyMap from './stepNameKeyMap'; const SetupStepper = () => { const { hasFinishedResolution, data: mcSetup } = useMCSetup(); - const { data: address, loaded: addressLoaded } = useStoreAddress(); - const { data: phone, loaded: phoneLoaded } = useGoogleMCPhoneNumber(); - const hasValidPhoneNumber = - phoneLoaded && phone?.isValid && phone?.isVerified; - - const hasValidAddress = - addressLoaded && - address?.isAddressFilled && - ! address?.isMCAddressDifferent; - - const hasConfirmedStoreRequirements = - hasValidPhoneNumber && hasValidAddress; - - const hasLoaded = - hasFinishedResolution && mcSetup && addressLoaded && phoneLoaded; + if ( ! hasFinishedResolution && ! mcSetup ) { + return ; + } if ( hasFinishedResolution && ! mcSetup ) { // this means error occurred, we just need to return null here, @@ -38,32 +24,14 @@ const SetupStepper = () => { return null; } - if ( ! hasLoaded ) { - return ; - } - - const { status } = mcSetup; - const { step } = mcSetup; - - // If the user has already completed the store requirements, but is currently still on the - // store requirements step, we should skip the store requirements step and go to the paid ads step. - // else they will get stuck on a non-existent step #3 - const currentStep = - step === 'store_requirements' && hasConfirmedStoreRequirements - ? 'paid_ads' - : step; + const { status, step } = mcSetup; if ( status === 'complete' ) { getHistory().replace( getNewPath( {}, '/google/dashboard' ) ); return null; } - return ( - - ); + return ; }; export default SetupStepper; diff --git a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js index 4484bcb8d9..5721020eab 100644 --- a/js/src/setup-mc/setup-stepper/saved-setup-stepper.js +++ b/js/src/setup-mc/setup-stepper/saved-setup-stepper.js @@ -14,6 +14,8 @@ import useTargetAudienceWithSuggestions from './useTargetAudienceWithSuggestions import useTargetAudienceFinalCountryCodes from '.~/hooks/useTargetAudienceFinalCountryCodes'; import useSettings from '.~/components/free-listings/configure-product-listings/useSettings'; import useShippingRates from '.~/hooks/useShippingRates'; +import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; +import useStoreAddress from '.~/hooks/useStoreAddress'; import useShippingTimes from '.~/hooks/useShippingTimes'; import useSaveShippingRates from '.~/hooks/useSaveShippingRates'; import useSaveShippingTimes from '.~/hooks/useSaveShippingTimes'; @@ -33,16 +35,26 @@ import { /** * @param {Object} props React props * @param {string} [props.savedStep] A saved step overriding the current step - * @param {boolean} [props.hasConfirmedStoreRequirements] Whether the store requirements have been confirmed * @fires gla_setup_mc with `{ triggered_by: 'step1-continue-button' | 'step2-continue-button', 'step3-continue-button', action: 'go-to-step2' | 'go-to-step3' | 'go-to-step4' }`. * @fires gla_setup_mc with `{ triggered_by: 'stepper-step1-button' | 'stepper-step2-button' | 'stepper-step3-button', action: 'go-to-step1' | 'go-to-step2' | 'go-to-step3' }`. */ -const SavedSetupStepper = ( { - savedStep, - hasConfirmedStoreRequirements = false, -} ) => { +const SavedSetupStepper = ( { savedStep } ) => { const [ step, setStep ] = useState( savedStep ); + const { data: address, loaded: addressLoaded } = useStoreAddress(); + const { data: phone, loaded: phoneLoaded } = useGoogleMCPhoneNumber(); + + const hasValidPhoneNumber = + phoneLoaded && phone?.isValid && phone?.isVerified; + + const hasValidAddress = + addressLoaded && + address?.isAddressFilled && + ! address?.isMCAddressDifferent; + + const hasConfirmedStoreRequirements = + hasValidPhoneNumber && hasValidAddress; + const { settings } = useSettings(); const { data: suggestedAudience } = useTargetAudienceWithSuggestions(); const { targetAudience, getFinalCountries } = @@ -217,25 +229,19 @@ const SavedSetupStepper = ( { ), onClick: handleStepClick, }, - ...( ! hasConfirmedStoreRequirements - ? [ - { - key: stepNameKeyMap.store_requirements, - label: __( - 'Confirm store requirements', - 'google-listings-and-ads' - ), - content: ( - - ), - onClick: handleStepClick, - }, - ] - : [] ), + { + key: stepNameKeyMap.store_requirements, + label: __( + 'Confirm store requirements', + 'google-listings-and-ads' + ), + content: ( + + ), + onClick: handleStepClick, + }, { key: stepNameKeyMap.paid_ads, label: __( 'Create a campaign', 'google-listings-and-ads' ),