diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.scss b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.scss
similarity index 100%
rename from plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.scss
rename to plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.scss
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.test.tsx b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.test.tsx
similarity index 100%
rename from plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.test.tsx
rename to plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.test.tsx
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.tsx b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx
similarity index 100%
rename from plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/CreateNewCampaignModal.tsx
rename to plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/index.ts b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/index.ts
similarity index 100%
rename from plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/CreateNewCampaignModal/index.ts
rename to plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/index.ts
diff --git a/plugins/woocommerce-admin/client/marketing/components/index.js b/plugins/woocommerce-admin/client/marketing/components/index.js
index dc971c75160..5d1c1014754 100644
--- a/plugins/woocommerce-admin/client/marketing/components/index.js
+++ b/plugins/woocommerce-admin/client/marketing/components/index.js
@@ -8,3 +8,4 @@ export { PluginCardBody, SmartPluginCardBody } from './PluginCardBody';
export { CardHeaderTitle } from './CardHeaderTitle';
export { CardHeaderDescription } from './CardHeaderDescription';
export { CenteredSpinner } from './CenteredSpinner';
+export { CreateNewCampaignModal } from './CreateNewCampaignModal';
diff --git a/plugins/woocommerce-admin/client/marketing/hooks/index.ts b/plugins/woocommerce-admin/client/marketing/hooks/index.ts
index a61a1a28ec7..3492d275ed0 100644
--- a/plugins/woocommerce-admin/client/marketing/hooks/index.ts
+++ b/plugins/woocommerce-admin/client/marketing/hooks/index.ts
@@ -1,4 +1,6 @@
+export { useIntroductionBanner } from './useIntroductionBanner';
export { useInstalledPlugins } from './useInstalledPlugins';
export { useRegisteredChannels } from './useRegisteredChannels';
export { useRecommendedChannels } from './useRecommendedChannels';
export { useCampaignTypes } from './useCampaignTypes';
+export { useCampaigns } from './useCampaigns';
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/useCampaigns.ts b/plugins/woocommerce-admin/client/marketing/hooks/useCampaigns.ts
similarity index 92%
rename from plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/useCampaigns.ts
rename to plugins/woocommerce-admin/client/marketing/hooks/useCampaigns.ts
index 01765b61b62..6b1672142b6 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/useCampaigns.ts
+++ b/plugins/woocommerce-admin/client/marketing/hooks/useCampaigns.ts
@@ -27,13 +27,10 @@ type UseCampaignsType = {
/**
* Custom hook to get campaigns.
*
- * @param page Page number. First page is `1`.
- * @param perPage Page size, i.e. number of records in one page.
+ * @param page Page number. Default is `1`.
+ * @param perPage Page size, i.e. number of records in one page. Default is `5`.
*/
-export const useCampaigns = (
- page: number,
- perPage: number
-): UseCampaignsType => {
+export const useCampaigns = ( page = 1, perPage = 5 ): UseCampaignsType => {
const { data: channels } = useRegisteredChannels();
return useSelect(
diff --git a/plugins/woocommerce-admin/client/marketing/hooks/useIntroductionBanner.ts b/plugins/woocommerce-admin/client/marketing/hooks/useIntroductionBanner.ts
new file mode 100644
index 00000000000..0eebcb471a1
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/hooks/useIntroductionBanner.ts
@@ -0,0 +1,45 @@
+/**
+ * External dependencies
+ */
+import { useDispatch, useSelect } from '@wordpress/data';
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
+import { recordEvent } from '@woocommerce/tracks';
+
+type UseIntroductionBanner = {
+ loading: boolean;
+ isIntroductionBannerDismissed: boolean;
+ dismissIntroductionBanner: () => void;
+};
+
+const OPTION_NAME_BANNER_DISMISSED =
+ 'woocommerce_marketing_overview_multichannel_banner_dismissed';
+const OPTION_VALUE_YES = 'yes';
+
+export const useIntroductionBanner = (): UseIntroductionBanner => {
+ const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
+
+ const dismissIntroductionBanner = () => {
+ updateOptions( {
+ [ OPTION_NAME_BANNER_DISMISSED ]: OPTION_VALUE_YES,
+ } );
+ recordEvent( 'marketing_multichannel_banner_dismissed', {} );
+ };
+
+ const { loading, data } = useSelect( ( select ) => {
+ const { getOption, hasFinishedResolution } =
+ select( OPTIONS_STORE_NAME );
+
+ return {
+ loading: ! hasFinishedResolution( 'getOption', [
+ OPTION_NAME_BANNER_DISMISSED,
+ ] ),
+ data: getOption( OPTION_NAME_BANNER_DISMISSED ),
+ };
+ }, [] );
+
+ return {
+ loading,
+ isIntroductionBannerDismissed: data === OPTION_VALUE_YES,
+ dismissIntroductionBanner,
+ };
+};
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.test.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.test.tsx
index 66513850819..aaa59396728 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.test.tsx
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.test.tsx
@@ -7,21 +7,23 @@ import userEvent from '@testing-library/user-event';
/**
* Internal dependencies
*/
-import { useCampaigns } from './useCampaigns';
-import { useCampaignTypes } from '~/marketing/hooks';
+import { useCampaignTypes, useCampaigns } from '~/marketing/hooks';
import { Campaigns } from './Campaigns';
-jest.mock( './useCampaigns', () => ( {
- useCampaigns: jest.fn(),
-} ) );
-
jest.mock( '~/marketing/hooks', () => ( {
+ useCampaigns: jest.fn(),
useCampaignTypes: jest.fn(),
} ) );
-jest.mock( './CreateNewCampaignModal', () => ( {
- CreateNewCampaignModal: () =>
Mocked CreateNewCampaignModal
,
-} ) );
+jest.mock( '~/marketing/components', () => {
+ const originalModule = jest.requireActual( '~/marketing/components' );
+
+ return {
+ __esModule: true,
+ ...originalModule,
+ CreateNewCampaignModal: () => Mocked CreateNewCampaignModal
,
+ };
+} );
/**
* Create a test campaign data object.
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.tsx
index 2861c79a5d9..d3fdcf71bca 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.tsx
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Campaigns/Campaigns.tsx
@@ -24,9 +24,11 @@ import {
/**
* Internal dependencies
*/
-import { CardHeaderTitle } from '~/marketing/components';
-import { useCampaigns } from './useCampaigns';
-import { CreateNewCampaignModal } from './CreateNewCampaignModal';
+import {
+ CardHeaderTitle,
+ CreateNewCampaignModal,
+} from '~/marketing/components';
+import { useCampaigns } from '~/marketing/hooks';
import './Campaigns.scss';
const tableCaption = __( 'Campaigns', 'woocommerce' );
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/Channels.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/Channels.tsx
index 992726a366f..7427f3f15f1 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/Channels.tsx
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/Channels.tsx
@@ -1,7 +1,13 @@
/**
* External dependencies
*/
-import { Fragment, useState } from '@wordpress/element';
+import {
+ Fragment,
+ useState,
+ forwardRef,
+ useImperativeHandle,
+ useRef,
+} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import {
Card,
@@ -32,72 +38,97 @@ type ChannelsProps = {
onInstalledAndActivated?: () => void;
};
-export const Channels: React.FC< ChannelsProps > = ( {
- registeredChannels,
- recommendedChannels,
- onInstalledAndActivated,
-} ) => {
- const hasRegisteredChannels = registeredChannels.length >= 1;
-
+export type ChannelsRef = {
/**
- * State to collapse / expand the recommended channels.
- * Initial state is expanded if there are no registered channels in first page load.
+ * Scroll into the "Add channels" section in the card.
+ * The section will be expanded, and the "Add channels" button will be in focus.
*/
- const [ expanded, setExpanded ] = useState( ! hasRegisteredChannels );
+ scrollIntoAddChannels: () => void;
+};
- return (
-
-
-
- { __( 'Channels', 'woocommerce' ) }
-
- { ! hasRegisteredChannels && (
-
- { __(
- 'Start by adding a channel to your store',
- 'woocommerce'
- ) }
-
- ) }
-
+export const Channels = forwardRef< ChannelsRef, ChannelsProps >(
+ (
+ { registeredChannels, recommendedChannels, onInstalledAndActivated },
+ ref
+ ) => {
+ const hasRegisteredChannels = registeredChannels.length >= 1;
- { /* Registered channels section. */ }
- { registeredChannels.map( ( el, idx ) => {
- return (
+ /**
+ * State to collapse / expand the recommended channels.
+ * Initial state is expanded if there are no registered channels in first page load.
+ */
+ const [ expanded, setExpanded ] = useState( ! hasRegisteredChannels );
+ const addChannelsButtonRef = useRef< HTMLButtonElement >( null );
+
+ useImperativeHandle(
+ ref,
+ () => ( {
+ scrollIntoAddChannels: () => {
+ setExpanded( true );
+ addChannelsButtonRef.current?.focus();
+ addChannelsButtonRef.current?.scrollIntoView( {
+ block: 'center',
+ } );
+ },
+ } ),
+ []
+ );
+
+ return (
+
+
+
+ { __( 'Channels', 'woocommerce' ) }
+
+ { ! hasRegisteredChannels && (
+
+ { __(
+ 'Start by adding a channel to your store',
+ 'woocommerce'
+ ) }
+
+ ) }
+
+
+ { /* Registered channels section. */ }
+ { registeredChannels.map( ( el, idx ) => (
{ idx !== registeredChannels.length - 1 && (
) }
- );
- } ) }
+ ) ) }
- { /* Recommended channels section. */ }
- { recommendedChannels.length >= 1 && (
-
- { !! hasRegisteredChannels && (
- <>
-
-
-
+ ) }
+
+ );
+ }
+);
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/index.ts b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/index.ts
index da0d9c56072..f0e4fcc3762 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/index.ts
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/Channels/index.ts
@@ -1 +1,2 @@
export { Channels } from './Channels';
+export type { ChannelsRef } from './Channels';
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.scss b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.scss
new file mode 100644
index 00000000000..99ac6dc8ad4
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.scss
@@ -0,0 +1,54 @@
+.woocommerce-marketing-introduction-banner {
+ & > div {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .woocommerce-marketing-introduction-banner-content {
+ flex: 1 0;
+ margin: 32px 20px 32px 40px;
+
+ .woocommerce-marketing-introduction-banner-title {
+ font-size: 20px;
+ line-height: 28px;
+ margin-bottom: $gap-smaller;
+ }
+
+ .woocommerce-marketing-introduction-banner-features {
+ color: $gray-700;
+
+ svg {
+ fill: $studio-woocommerce-purple-50;
+ }
+ }
+
+ .woocommerce-marketing-introduction-banner-buttons {
+ margin-top: $gap;
+ }
+ }
+
+ .woocommerce-marketing-introduction-banner-illustration {
+ flex: 0 0 270px;
+ background: linear-gradient(90deg, rgba(247, 237, 247, 0) 5.31%, rgba(196, 152, 217, 0.12) 77.75%),
+ linear-gradient(90deg, rgba(247, 237, 247, 0) 22%, rgba(196, 152, 217, 0.12) 84.6%);
+
+ .woocommerce-marketing-introduction-banner-image-placeholder {
+ width: 100%;
+ height: 100%;
+ background: center / contain no-repeat;
+ }
+
+ .woocommerce-marketing-introduction-banner-close-button {
+ position: absolute;
+ top: $gap-small;
+ right: $gap;
+ padding: 0;
+ }
+
+ img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+ }
+}
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.tsx
new file mode 100644
index 00000000000..829152f03dd
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/IntroductionBanner.tsx
@@ -0,0 +1,152 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useState } from '@wordpress/element';
+import { Card, Flex, FlexItem, FlexBlock, Button } from '@wordpress/components';
+import { Icon, trendingUp, megaphone, closeSmall } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import { CreateNewCampaignModal } from '~/marketing/components';
+import {
+ useRegisteredChannels,
+ useRecommendedChannels,
+} from '~/marketing/hooks';
+import './IntroductionBanner.scss';
+import wooIconUrl from './woo.svg';
+import illustrationUrl from './illustration.svg';
+
+type IntroductionBannerProps = {
+ onDismissClick: () => void;
+ onAddChannelsClick: () => void;
+};
+
+export const IntroductionBanner = ( {
+ onDismissClick,
+ onAddChannelsClick,
+}: IntroductionBannerProps ) => {
+ const [ isModalOpen, setModalOpen ] = useState( false );
+ const { data: dataRegistered } = useRegisteredChannels();
+ const { data: dataRecommended } = useRecommendedChannels();
+
+ const showCreateCampaignButton = !! dataRegistered?.length;
+
+ /**
+ * Boolean to display the "Add channels" button in the introduction banner.
+ *
+ * This depends on the number of registered channels,
+ * because if there are no registered channels,
+ * the Channels card will not have the "Add channels" toggle button,
+ * and it does not make sense to display the "Add channels" button in this introduction banner
+ * that will do nothing upon click.
+ *
+ * If there are registered channels and recommended channels,
+ * the Channels card will display the "Add channels" toggle button,
+ * and clicking on the "Add channels" button in this introduction banner
+ * will scroll to the button in Channels card.
+ */
+ const showAddChannelsButton =
+ !! dataRegistered?.length && !! dataRecommended?.length;
+
+ return (
+
+
+
+ { __(
+ 'Reach new customers and increase sales without leaving WooCommerce',
+ 'woocommerce'
+ ) }
+
+
+
+
+
+
+ { __(
+ 'Reach customers on other sales channels',
+ 'woocommerce'
+ ) }
+
+
+
+
+
+
+
+ { __(
+ 'Advertise with marketing campaigns',
+ 'woocommerce'
+ ) }
+
+
+
+
+
+
+
+ { __( 'Built by WooCommerce', 'woocommerce' ) }
+
+
+
+
+ { ( showCreateCampaignButton || showAddChannelsButton ) && (
+
+ { showCreateCampaignButton && (
+ {
+ setModalOpen( true );
+ } }
+ >
+ { __( 'Create a campaign', 'woocommerce' ) }
+
+ ) }
+ { showAddChannelsButton && (
+
+ { __( 'Add channels', 'woocommerce' ) }
+
+ ) }
+
+ ) }
+ { isModalOpen && (
+
setModalOpen( false ) }
+ />
+ ) }
+
+
+
+ );
+};
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/illustration.svg b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/illustration.svg
new file mode 100644
index 00000000000..3a00d63416c
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/illustration.svg
@@ -0,0 +1,68 @@
+
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/index.ts b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/index.ts
new file mode 100644
index 00000000000..8ae35a6ca33
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/index.ts
@@ -0,0 +1 @@
+export { IntroductionBanner } from './IntroductionBanner';
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/woo.svg b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/woo.svg
new file mode 100644
index 00000000000..fb4a4205043
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/IntroductionBanner/woo.svg
@@ -0,0 +1,15 @@
+
+
diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx
index 3922d1cba32..94784f01468 100644
--- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx
+++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
+import { useRef } from '@wordpress/element';
import { useUser } from '@woocommerce/data';
/**
@@ -10,19 +11,28 @@ import '~/marketing/data';
import '~/marketing/data-multichannel';
import { CenteredSpinner } from '~/marketing/components';
import {
+ useIntroductionBanner,
+ useCampaigns,
useRegisteredChannels,
useRecommendedChannels,
useCampaignTypes,
} from '~/marketing/hooks';
import { getAdminSetting } from '~/utils/admin-settings';
+import { IntroductionBanner } from './IntroductionBanner';
import { Campaigns } from './Campaigns';
-import { Channels } from './Channels';
+import { Channels, ChannelsRef } from './Channels';
import { InstalledExtensions } from './InstalledExtensions';
import { DiscoverTools } from './DiscoverTools';
import { LearnMarketing } from './LearnMarketing';
import './MarketingOverviewMultichannel.scss';
export const MarketingOverviewMultichannel: React.FC = () => {
+ const {
+ loading: loadingIntroductionBanner,
+ isIntroductionBannerDismissed,
+ dismissIntroductionBanner,
+ } = useIntroductionBanner();
+ const { loading: loadingCampaigns, meta: metaCampaigns } = useCampaigns();
const {
loading: loadingCampaignTypes,
data: dataCampaignTypes,
@@ -36,8 +46,11 @@ export const MarketingOverviewMultichannel: React.FC = () => {
const { loading: loadingRecommended, data: dataRecommended } =
useRecommendedChannels();
const { currentUserCan } = useUser();
+ const channelsRef = useRef< ChannelsRef >( null );
if (
+ loadingIntroductionBanner ||
+ ( loadingCampaigns && metaCampaigns?.total === undefined ) ||
( loadingCampaignTypes && ! dataCampaignTypes ) ||
( loadingRegistered && ! dataRegistered ) ||
( loadingRecommended && ! dataRecommended )
@@ -45,6 +58,11 @@ export const MarketingOverviewMultichannel: React.FC = () => {
return ;
}
+ const shouldShowCampaigns = !! (
+ dataRegistered?.length &&
+ ( isIntroductionBannerDismissed || metaCampaigns?.total )
+ );
+
const shouldShowExtensions =
getAdminSetting( 'allowMarketplaceSuggestions', false ) &&
currentUserCan( 'install_plugins' );
@@ -56,10 +74,19 @@ export const MarketingOverviewMultichannel: React.FC = () => {
return (
- { !! dataRegistered?.length && }
+ { ! isIntroductionBannerDismissed && (
+ {
+ channelsRef.current?.scrollIntoAddChannels();
+ } }
+ />
+ ) }
+ { shouldShowCampaigns && }
{ !! ( dataRegistered && dataRecommended ) &&
!! ( dataRegistered.length || dataRecommended.length ) && (