diff --git a/js/src/components/experience-rating-banner/banner.js b/js/src/components/experience-rating-banner/banner.js new file mode 100644 index 0000000000..77981b278b --- /dev/null +++ b/js/src/components/experience-rating-banner/banner.js @@ -0,0 +1,185 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { Notice, Icon } from '@wordpress/components'; +import { external as externalIcon } from '@wordpress/icons'; +import { useState, useEffect, useMemo } from '@wordpress/element'; +import { store as preferencesStore } from '@wordpress/preferences'; + +/** + * Internal dependencies + */ +import { recordGlaEvent } from '~/utils/tracks'; +import { BANNER_DISMISSED_KEY } from './constants'; +import { + REPORT_SOURCE_PAID, + PREFERENCES_STORE_NAMESPACE, + APP_RATINGS_BANNER_CONTEXT, +} from '~/constants'; +import AppButton from '~/components/app-button'; +import useMCProductStatistics from '~/hooks/useMCProductStatistics'; +import useProductsReport from '~/pages/reports/products/useProductsReport'; +import FeedbackModal from './feedback-modal'; +import './index.scss'; + +/** + * When the experience rating banner is displayed to the user. + * + * @event gla_app_ratings_shown + * @property {string} context The context in which the event is triggered. + */ + +/** + * When the user clicks the "Good" button on the banner. + * + * @event gla_app_ratings_good_clicked + * @property {string} context The context in which the event is triggered. + */ + +/** + * When the feedback modal is closed by the user. + * + * @event gla_app_ratings_close + * @property {string} context The context in which the event is triggered. + */ + +/** + * When the user clicks the "Need help" button on the banner. + * + * @event gla_app_ratings_need_help_clicked + * @property {string} context The context in which the event is triggered. + */ + +/** + * Banner component. + * + * Displays a dismissible banner asking users to rate their experience with Google for WooCommerce. + * Handles user interactions such as clicking "Good", "Need help", and dismissing the banner, + * and records corresponding events. Shows a feedback modal when "Good" is clicked. + * + * @return {JSX.Element|null} The Banner component, or null if dismissed. + * + * @fires gla_app_ratings_shown When the banner is shown. + * @fires gla_app_ratings_good_clicked When the "Good" button is clicked. + * @fires gla_app_ratings_close When the feedback modal is closed. + * @fires gla_app_ratings_need_help_clicked When the "Need help" button is clicked. + */ +const Banner = () => { + const { data: statisticsData } = useMCProductStatistics(); + const { data: productsReport } = useProductsReport( REPORT_SOURCE_PAID ); + const [ showModal, setShowModal ] = useState( false ); + const { set } = useDispatch( preferencesStore ) || {}; + + const shouldDisplayBanner = useMemo( () => { + const statistics = statisticsData?.statistics || {}; + + if ( Object.keys( statistics ).length === 0 ) { + return false; + } + + const totalProducts = Object.values( statistics ).reduce( + ( total, value ) => total + value, + 0 + ); + + if ( ! totalProducts ) { + return false; + } + + const notSyncedProducts = statistics.not_synced; + const activeProducts = statistics.active; + const conversions = productsReport?.totals?.conversions?.value || 0; + + const hasEnoughActiveProducts = + ( activeProducts / totalProducts ) * 100 >= 70; + const hasAllProductsSynced = + notSyncedProducts === 0 && activeProducts > 0; + const hasConversions = conversions > 0; + + return ( + hasEnoughActiveProducts && hasAllProductsSynced && hasConversions + ); + }, [ statisticsData, productsReport ] ); + + useEffect( () => { + if ( shouldDisplayBanner ) { + recordGlaEvent( 'gla_app_ratings_shown', { + context: APP_RATINGS_BANNER_CONTEXT, + } ); + } + }, [ shouldDisplayBanner ] ); + + if ( ! shouldDisplayBanner ) { + return null; + } + + const handleGoodOnClick = () => { + recordGlaEvent( 'gla_app_ratings_good_clicked', { + context: APP_RATINGS_BANNER_CONTEXT, + } ); + setShowModal( true ); + }; + + const handleRequestClose = () => { + recordGlaEvent( 'gla_app_ratings_close', { + context: APP_RATINGS_BANNER_CONTEXT, + } ); + setShowModal( false ); + }; + + const handleNeedHelpOnClick = () => { + recordGlaEvent( 'gla_app_ratings_need_help_clicked', { + context: APP_RATINGS_BANNER_CONTEXT, + } ); + }; + + const dismissBanner = () => { + set( PREFERENCES_STORE_NAMESPACE, BANNER_DISMISSED_KEY, true ); + }; + + return ( +
+ { showModal && ( + + ) } + +

+ { __( + 'How was your experience with Google for WooCommerce?', + 'google-listings-and-ads' + ) } +

+ +
+ + { __( 'Good', 'google-listings-and-ads' ) } + + + } + iconPosition="right" + iconSize={ 16 } + > + { __( 'Need help', 'google-listings-and-ads' ) } + +
+
+
+ ); +}; + +export default Banner; diff --git a/js/src/components/experience-rating-banner/constants.js b/js/src/components/experience-rating-banner/constants.js new file mode 100644 index 0000000000..de5970d7db --- /dev/null +++ b/js/src/components/experience-rating-banner/constants.js @@ -0,0 +1 @@ +export const BANNER_DISMISSED_KEY = 'experience-rating-banner-dismissed'; diff --git a/js/src/components/experience-rating-banner/index.js b/js/src/components/experience-rating-banner/index.js index 8ec62d8a3d..10d65b3ff7 100644 --- a/js/src/components/experience-rating-banner/index.js +++ b/js/src/components/experience-rating-banner/index.js @@ -1,193 +1,30 @@ -/** - * External dependencies - */ -import { Notice, Icon } from '@wordpress/components'; -import { useDispatch } from '@wordpress/data'; -import { useState, useEffect } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { external as externalIcon } from '@wordpress/icons'; -import { store as preferencesStore } from '@wordpress/preferences'; - /** * Internal dependencies */ -import AppButton from '../app-button'; -import { - REPORT_SOURCE_PAID, - PREFERENCES_STORE_NAMESPACE, - APP_RATINGS_BANNER_CONTEXT, -} from '~/constants'; -import useMCProductStatistics from '~/hooks/useMCProductStatistics'; -import useGoogleMCAccount from '~/hooks/useGoogleMCAccount'; -import useProductsReport from '~/pages/reports/products/useProductsReport'; +import Banner from './banner'; import usePreference from '~/hooks/usePreference'; -import FeedbackModal from './feedback-modal'; -import { recordGlaEvent } from '~/utils/tracks'; +import useGoogleAdsAccount from '~/hooks/useGoogleAdsAccount'; +import useGoogleMCAccount from '~/hooks/useGoogleMCAccount'; +import { BANNER_DISMISSED_KEY } from './constants'; import './index.scss'; -const BANNER_DISMISSED_KEY = 'experience-rating-banner-dismissed'; - -/** - * When the experience rating banner is displayed to the user. - * - * @event gla_app_ratings_shown - * @property {string} context The context in which the event is triggered. - */ - -/** - * When the user clicks the "Good" button on the banner. - * - * @event gla_app_ratings_good_clicked - * @property {string} context The context in which the event is triggered. - */ - -/** - * When the feedback modal is closed by the user. - * - * @event gla_app_ratings_close - * @property {string} context The context in which the event is triggered. - */ - -/** - * When the user clicks the "Need help" button on the banner. - * - * @event gla_app_ratings_need_help_clicked - * @property {string} context The context in which the event is triggered. - */ - /** * ExperienceRatingBanner component. * - * Displays a dismissible banner asking users to rate their experience with Google for WooCommerce. - * Handles user interactions such as clicking "Good", "Need help", and dismissing the banner, - * and records corresponding events. Shows a feedback modal when "Good" is clicked. - * - * @return {JSX.Element|null} The ExperienceRatingBanner component, or null if dismissed. + * Displays a banner asking users to rate their experience with Google for WooCommerce. * - * @fires gla_app_ratings_shown When the banner is shown. - * @fires gla_app_ratings_good_clicked When the "Good" button is clicked. - * @fires gla_app_ratings_close When the feedback modal is closed. - * @fires gla_app_ratings_need_help_clicked When the "Need help" button is clicked. + * @return {JSX.Element|null} The ExperienceRatingBanner component, or null if dismissed or Ads account is disconnected or the MC account is not ready. */ const ExperienceRatingBanner = () => { - const { data: statisticsData } = useMCProductStatistics(); + const { hasGoogleAdsConnection } = useGoogleAdsAccount(); const { isReady: isMCAccountReady } = useGoogleMCAccount(); - const { data: productsReport } = useProductsReport( REPORT_SOURCE_PAID ); - const [ showModal, setShowModal ] = useState( false ); - const { set } = useDispatch( preferencesStore ); const isDismissed = usePreference( BANNER_DISMISSED_KEY ); - const statistics = statisticsData?.statistics || {}; - - const shouldDisplayBanner = () => { - if ( Object.keys( statistics ).length === 0 ) { - return false; - } - - const totalProducts = Object.values( statistics ).reduce( - ( total, value ) => total + value, - 0 - ); - - if ( ! totalProducts ) { - return false; - } - - const notSyncedProducts = statistics.not_synced; - const activeProducts = statistics.active; - const conversions = productsReport?.totals?.conversions?.value || 0; - const hasEnoughActiveProducts = - ( activeProducts / totalProducts ) * 100 >= 70; - const hasAllProductsSynced = - notSyncedProducts === 0 && activeProducts > 0; - const hasConversions = conversions > 0; - - return ( - hasEnoughActiveProducts && - hasAllProductsSynced && - isMCAccountReady && - hasConversions - ); - }; - - // Fire event when banner is shown. - useEffect( () => { - if ( ! isDismissed ) { - recordGlaEvent( 'gla_app_ratings_shown', { - context: APP_RATINGS_BANNER_CONTEXT, - } ); - } - }, [ isDismissed ] ); - - if ( ! shouldDisplayBanner() || isDismissed ) { + if ( ! hasGoogleAdsConnection || ! isMCAccountReady || isDismissed ) { return null; } - const handleGoodOnClick = () => { - recordGlaEvent( 'gla_app_ratings_good_clicked', { - context: APP_RATINGS_BANNER_CONTEXT, - } ); - setShowModal( true ); - }; - - const handleRequestClose = () => { - recordGlaEvent( 'gla_app_ratings_close', { - context: APP_RATINGS_BANNER_CONTEXT, - } ); - setShowModal( false ); - }; - - const handleNeedHelpOnClick = () => { - recordGlaEvent( 'gla_app_ratings_need_help_clicked', { - context: APP_RATINGS_BANNER_CONTEXT, - } ); - }; - - const dismissBanner = () => { - set( PREFERENCES_STORE_NAMESPACE, BANNER_DISMISSED_KEY, true ); - }; - - return ( -
- { showModal && ( - - ) } - -

- { __( - 'How was your experience with Google for WooCommerce?', - 'google-listings-and-ads' - ) } -

- -
- - { __( 'Good', 'google-listings-and-ads' ) } - - - } - iconPosition="right" - iconSize={ 16 } - > - { __( 'Need help', 'google-listings-and-ads' ) } - -
-
-
- ); + return ; }; export default ExperienceRatingBanner; diff --git a/src/Tracking/README.md b/src/Tracking/README.md index e58a20181b..bd13bd6194 100644 --- a/src/Tracking/README.md +++ b/src/Tracking/README.md @@ -188,23 +188,23 @@ Clicking on the button to disconnect the Google Ads account. #### Emitters - [`BillingSetupCard`](../../js/src/components/paid-ads/billing-card/billing-setup-card.js#L39) When the user clicks on the button to set up billing in Google Ads. -### [`gla_app_ratings_close`](../../js/src/components/experience-rating-banner/index.js#L44) +### [`gla_app_ratings_close`](../../js/src/components/experience-rating-banner/banner.js#L41) When the feedback modal is closed by the user. #### Properties | name | type | description | | ---- | ---- | ----------- | `context` | `string` | The context in which the event is triggered. #### Emitters -- [`ExperienceRatingBanner`](../../js/src/components/experience-rating-banner/index.js#L72) When the feedback modal is closed. +- [`Banner`](../../js/src/components/experience-rating-banner/banner.js#L69) When the feedback modal is closed. -### [`gla_app_ratings_good_clicked`](../../js/src/components/experience-rating-banner/index.js#L37) +### [`gla_app_ratings_good_clicked`](../../js/src/components/experience-rating-banner/banner.js#L34) When the user clicks the "Good" button on the banner. #### Properties | name | type | description | | ---- | ---- | ----------- | `context` | `string` | The context in which the event is triggered. #### Emitters -- [`ExperienceRatingBanner`](../../js/src/components/experience-rating-banner/index.js#L72) When the "Good" button is clicked. +- [`Banner`](../../js/src/components/experience-rating-banner/banner.js#L69) When the "Good" button is clicked. ### [`gla_app_ratings_maybe_later_clicked`](../../js/src/components/experience-rating-banner/feedback-modal.js#L16) @@ -215,14 +215,14 @@ When the user clicks the "Good" button on the banner. #### Emitters - [`FeedbackModal`](../../js/src/components/experience-rating-banner/feedback-modal.js#L39) Fired when the user clicks the "Maybe later" button. -### [`gla_app_ratings_need_help_clicked`](../../js/src/components/experience-rating-banner/index.js#L51) +### [`gla_app_ratings_need_help_clicked`](../../js/src/components/experience-rating-banner/banner.js#L48) When the user clicks the "Need help" button on the banner. #### Properties | name | type | description | | ---- | ---- | ----------- | `context` | `string` | The context in which the event is triggered. #### Emitters -- [`ExperienceRatingBanner`](../../js/src/components/experience-rating-banner/index.js#L72) When the "Need help" button is clicked. +- [`Banner`](../../js/src/components/experience-rating-banner/banner.js#L69) When the "Need help" button is clicked. ### [`gla_app_ratings_rate_clicked`](../../js/src/components/experience-rating-banner/feedback-modal.js#L21) @@ -233,14 +233,14 @@ When the user clicks the "Need help" button on the banner. #### Emitters - [`FeedbackModal`](../../js/src/components/experience-rating-banner/feedback-modal.js#L39) Fired when the user clicks the "Rate us" button. -### [`gla_app_ratings_shown`](../../js/src/components/experience-rating-banner/index.js#L30) +### [`gla_app_ratings_shown`](../../js/src/components/experience-rating-banner/banner.js#L27) When the experience rating banner is displayed to the user. #### Properties | name | type | description | | ---- | ---- | ----------- | `context` | `string` | The context in which the event is triggered. #### Emitters -- [`ExperienceRatingBanner`](../../js/src/components/experience-rating-banner/index.js#L72) When the banner is shown. +- [`Banner`](../../js/src/components/experience-rating-banner/banner.js#L69) When the banner is shown. ### [`gla_attribute_mapping_create_rule`](../../js/src/pages/attribute-mapping/attribute-mapping-rule-modal.js#L32) Creates the rule successfully