Add order attribution install banner in core analytics overview (#51672)

* Implement order attribution install banner in analytics overview page

* Add a hook for setting banner dismissed option

* Use the hook the decide whether to show banner and call dismiss funtion
when clicking dismiss CTA

* Do not show banner when the plugin is already installed

* Add event tracking for banner viewed and clicked

* UI adjustments based on PR reviews

* Move client/order-attribution to client/order-attribution-install-banner

* Prevent the banner viewed event from being sent again wehn rerendering

* Add a comment to the hook useOrderAttributionInstallBanner

* Remove the prop of eventContext since it uses the default value

* Rename the css class name to make it more general
This commit is contained in:
Ian Yu-Hsun Lin 2024-10-03 14:27:35 +08:00 committed by GitHub
parent 27fc8f52c0
commit 781a7fb3db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 309 additions and 0 deletions

View File

@ -7,6 +7,7 @@ import { Spinner } from '@woocommerce/components';
/**
* Internal dependencies
*/
import { OrderAttributionInstallBanner } from '~/order-attribution-install-banner';
import './style.scss';
const CustomizableDashboard = lazy( () =>
@ -19,6 +20,7 @@ class Dashboard extends Component {
return (
<Suspense fallback={ <Spinner /> }>
<OrderAttributionInstallBanner />
<CustomizableDashboard query={ query } path={ path } />
</Suspense>
);

View File

@ -0,0 +1 @@
export * from './order-attribution-install-banner';

View File

@ -0,0 +1,62 @@
const OrderAttributionInstallBannerImage = () => (
<svg
width="156"
height="159"
viewBox="0 0 156 159"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="156" height="159" fill="white" />
<rect width="156" height="156" fill="white" />
<g clipPath="url(#clip0_2736_17062)">
<rect
width="142"
height="142"
transform="translate(7 7)"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20.0977 114.983C4.18437 87.4207 6.33647 56.3862 24.9045 45.666C34.5831 40.078 46.8115 41.1124 58.5555 47.2872C58.4035 35.8329 62.3202 26.2999 70.1879 21.7576C79.3736 16.4542 91.7579 19.2162 103.017 27.7639C101.298 19.4628 103.233 11.9838 108.644 8.86007C116.335 4.41967 128.136 10.4613 135.002 22.3544C141.869 34.2475 141.2 47.4884 133.509 51.9288C130.491 53.6712 126.841 53.7996 123.103 52.5938C134.128 74.6399 132.448 97.8318 118.694 105.773C112.641 109.267 105.198 109.26 97.6045 106.402C99.5284 123.573 94.3619 138.649 82.5317 145.479C63.9637 156.199 36.011 142.546 20.0977 114.983Z"
fill="#F9F6FF"
/>
<path
d="M128.714 49.0745C131.36 49.0745 133.785 49.7713 136 50.7842V41.1369C136 35.1931 132.613 31.8594 126.634 31.8594H29.3611C23.3874 31.854 20 35.1877 20 41.1315V99.2728H65.7462C63.4879 102.639 61.3157 106.621 61.3157 111.284C61.3157 120.604 68.6067 128.161 77.957 128.161C87.3073 128.161 94.5983 120.604 94.5983 111.284C94.5983 106.621 92.4261 102.639 90.1678 99.2728H136V80.3587C133.785 81.3717 131.36 82.0684 128.714 82.0684C119.412 82.0684 111.869 74.8436 111.869 65.5661C111.869 56.2886 119.412 49.0638 128.714 49.0638V49.0745Z"
fill="#BEA0F2"
/>
<path
d="M68.268 75.1553H29.5547V79.9789H68.268V75.1553Z"
fill="#674399"
/>
<path
d="M53.7505 84.8018H29.5547V89.6254H53.7505V84.8018Z"
fill="#674399"
/>
<path
d="M134.864 107.13C132.799 105.495 112.05 89.6309 112.05 89.6309C112.05 89.6309 109.388 115.561 109.162 118.182C108.937 120.802 111.485 121.955 113.539 120.224C116.394 117.828 122.207 113.111 122.207 113.111C122.207 113.111 129.637 112.131 133.342 111.707C136.014 111.402 136.923 108.765 134.853 107.13H134.864Z"
fill="#674399"
/>
<path
d="M68.268 41.3887H29.5547V70.3307H68.268V41.3887Z"
fill="#674399"
/>
<path
d="M29.5547 70.3307C45.242 58.6073 58.3995 48.9761 68.268 41.3887V70.3307H29.5547Z"
fill="#3C2861"
/>
</g>
<defs>
<clipPath id="clip0_2736_17062">
<rect
width="142"
height="142"
fill="white"
transform="translate(7 7)"
/>
</clipPath>
</defs>
</svg>
);
export default OrderAttributionInstallBannerImage;

View File

@ -0,0 +1,106 @@
/**
* External dependencies
*/
import { Button, Card, CardBody } from '@wordpress/components';
import { useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Text } from '@woocommerce/experimental';
import { recordEvent } from '@woocommerce/tracks';
import { getPath } from '@woocommerce/navigation';
/**
* Internal dependencies
*/
import OrderAttributionInstallBannerImage from './order-attribution-install-banner-image';
import useOrderAttributionInstallBanner from './use-order-attribution-install-banner';
import './style.scss';
export const OrderAttributionInstallBanner = ( {
bannerImage = <OrderAttributionInstallBannerImage />,
eventContext = 'analytics-overview',
} ) => {
const { isDismissed, dismiss, shouldShowBanner } =
useOrderAttributionInstallBanner();
useEffect( () => {
if ( ! shouldShowBanner || isDismissed ) {
return;
}
recordEvent( 'order_attribution_install_banner_viewed', {
path: getPath(),
context: eventContext,
} );
}, [ eventContext, shouldShowBanner, isDismissed ] );
if ( ! shouldShowBanner || isDismissed ) {
return null;
}
return (
<Card
size="medium"
className="woocommerce-order-attribution-install-banner"
>
<CardBody className="woocommerce-order-attribution-install-banner__body">
<div className="woocommerce-order-attribution-install-banner__image_container">
{ bannerImage }
</div>
<div className="woocommerce-order-attribution-install-banner__text_container">
<div className="woocommerce-order-attribution-install-banner__text-badge">
<Text
className="woocommerce-order-attribution-install-banner__text-description"
as="p"
size="12"
align="center"
>
{ __( 'New', 'woocommerce' ) }
</Text>
</div>
<Text
className="woocommerce-order-attribution-install-banner__text-title"
as="p"
size="16"
>
{ __(
'Discover what drives your sales',
'woocommerce'
) }
</Text>
<Text
className="woocommerce-order-attribution-install-banner__text-description"
as="p"
size="12"
>
{ __(
'Understand what truly drives revenue with our powerful order attribution extension. Use it to track your sales journey, identify your most effective marketing channels, and optimize your sales strategy.',
'woocommerce'
) }
</Text>
<div>
<Button
href="https://woocommerce.com/products/woocommerce-analytics"
variant="primary"
onClick={ () =>
recordEvent(
'order_attribution_install_banner_clicked',
{
path: getPath(),
context: eventContext,
}
)
}
>
{ __( 'Try it now', 'woocommerce' ) }
</Button>
<Button
variant="tertiary"
onClick={ () => dismiss( eventContext ) }
>
{ __( 'Dismiss', 'woocommerce' ) }
</Button>
</div>
</div>
</CardBody>
</Card>
);
};

View File

@ -0,0 +1,62 @@
.woocommerce-order-attribution-install-banner {
margin: 0 15px 10px 0;
animation: isLoaded;
animation-duration: 250ms;
&.components-card {
box-shadow: none;
border: 1px solid $table-border;
border-radius: 8px;
}
.woocommerce-order-attribution-install-banner__body {
display: flex;
align-items: center;
@media (max-width: $break-small) {
flex-direction: column;
}
}
.woocommerce-order-attribution-install-banner__image_container {
display: flex;
padding-left: 15px;
}
.woocommerce-order-attribution-install-banner__text_container {
margin-inline: 24px;
> * {
margin-block: 1rem;
}
p {
margin-block: 0.1rem;
}
}
.woocommerce-order-attribution-install-banner__text-badge {
display: inline-flex;
justify-content: center;
background: $studio-gray-0;
border-radius: 2px;
padding: 2px 5px;
p {
color: $studio-gray-80;
}
}
.woocommerce-order-attribution-install-banner__text-title {
color: $studio-gray-90;
line-height: 24px;
font-weight: 600;
}
.woocommerce-order-attribution-install-banner__text-description {
color: $studio-gray-90;
line-height: 16px;
max-width: 580px;
}
}

View File

@ -0,0 +1,75 @@
/**
* External dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import {
OPTIONS_STORE_NAME,
PLUGINS_STORE_NAME,
useUser,
} from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { getPath } from '@woocommerce/navigation';
const OPTION_NAME_BANNER_DISMISSED =
'woocommerce_order_attribution_install_banner_dismissed';
const OPTION_VALUE_YES = 'yes';
/**
* A utility hook designed specifically for the order attribution install banner,
* which determines if the banner should be displayed, checks if it has been dismissed, and provides a function to dismiss it.
*/
const useOrderAttributionInstallBanner = () => {
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const { currentUserCan } = useUser();
const dismiss = ( eventContext = 'analytics-overview' ) => {
updateOptions( {
[ OPTION_NAME_BANNER_DISMISSED ]: OPTION_VALUE_YES,
} );
recordEvent( 'order_attribution_install_banner_dismissed', {
path: getPath(),
context: eventContext,
} );
};
const { canUserInstallPlugins, orderAttributionInstallState } = useSelect(
( select ) => {
const { getPluginInstallState } = select( PLUGINS_STORE_NAME );
const installState = getPluginInstallState(
'woocommerce-analytics'
);
return {
orderAttributionInstallState: installState,
canUserInstallPlugins: currentUserCan( 'install_plugins' ),
};
},
[ currentUserCan ]
);
const { loading, isBannerDismissed } = useSelect( ( select ) => {
const { getOption, hasFinishedResolution } =
select( OPTIONS_STORE_NAME );
return {
loading: ! hasFinishedResolution( 'getOption', [
OPTION_NAME_BANNER_DISMISSED,
] ),
isBannerDismissed: getOption( OPTION_NAME_BANNER_DISMISSED ),
};
}, [] );
return {
loading,
isDismissed: isBannerDismissed === OPTION_VALUE_YES,
dismiss,
shouldShowBanner:
! loading &&
canUserInstallPlugins &&
! [ 'installed', 'activated' ].includes(
orderAttributionInstallState
),
};
};
export default useOrderAttributionInstallBanner;

View File

@ -223,6 +223,7 @@ class Options extends \WC_REST_Data_Controller {
'woocommerce_private_link',
'woocommerce_share_key',
'woocommerce_show_lys_tour',
'woocommerce_order_attribution_install_banner_dismissed',
// WC Test helper options.
'wc-admin-test-helper-rest-api-filters',
'wc_admin_helper_feature_values',