Add introduction banner card into multichannel marketing page (#37110)

This commit is contained in:
Gan Eng Chin 2023-03-20 19:25:01 +08:00 committed by GitHub
commit bebad071a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 484 additions and 82 deletions

View File

@ -8,3 +8,4 @@ export { PluginCardBody, SmartPluginCardBody } from './PluginCardBody';
export { CardHeaderTitle } from './CardHeaderTitle'; export { CardHeaderTitle } from './CardHeaderTitle';
export { CardHeaderDescription } from './CardHeaderDescription'; export { CardHeaderDescription } from './CardHeaderDescription';
export { CenteredSpinner } from './CenteredSpinner'; export { CenteredSpinner } from './CenteredSpinner';
export { CreateNewCampaignModal } from './CreateNewCampaignModal';

View File

@ -1,4 +1,6 @@
export { useIntroductionBanner } from './useIntroductionBanner';
export { useInstalledPlugins } from './useInstalledPlugins'; export { useInstalledPlugins } from './useInstalledPlugins';
export { useRegisteredChannels } from './useRegisteredChannels'; export { useRegisteredChannels } from './useRegisteredChannels';
export { useRecommendedChannels } from './useRecommendedChannels'; export { useRecommendedChannels } from './useRecommendedChannels';
export { useCampaignTypes } from './useCampaignTypes'; export { useCampaignTypes } from './useCampaignTypes';
export { useCampaigns } from './useCampaigns';

View File

@ -27,13 +27,10 @@ type UseCampaignsType = {
/** /**
* Custom hook to get campaigns. * Custom hook to get campaigns.
* *
* @param page Page number. First page is `1`. * @param page Page number. Default is `1`.
* @param perPage Page size, i.e. number of records in one page. * @param perPage Page size, i.e. number of records in one page. Default is `5`.
*/ */
export const useCampaigns = ( export const useCampaigns = ( page = 1, perPage = 5 ): UseCampaignsType => {
page: number,
perPage: number
): UseCampaignsType => {
const { data: channels } = useRegisteredChannels(); const { data: channels } = useRegisteredChannels();
return useSelect( return useSelect(

View File

@ -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,
};
};

View File

@ -7,21 +7,23 @@ import userEvent from '@testing-library/user-event';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { useCampaigns } from './useCampaigns'; import { useCampaignTypes, useCampaigns } from '~/marketing/hooks';
import { useCampaignTypes } from '~/marketing/hooks';
import { Campaigns } from './Campaigns'; import { Campaigns } from './Campaigns';
jest.mock( './useCampaigns', () => ( {
useCampaigns: jest.fn(),
} ) );
jest.mock( '~/marketing/hooks', () => ( { jest.mock( '~/marketing/hooks', () => ( {
useCampaigns: jest.fn(),
useCampaignTypes: jest.fn(), useCampaignTypes: jest.fn(),
} ) ); } ) );
jest.mock( './CreateNewCampaignModal', () => ( { jest.mock( '~/marketing/components', () => {
const originalModule = jest.requireActual( '~/marketing/components' );
return {
__esModule: true,
...originalModule,
CreateNewCampaignModal: () => <div>Mocked CreateNewCampaignModal</div>, CreateNewCampaignModal: () => <div>Mocked CreateNewCampaignModal</div>,
} ) ); };
} );
/** /**
* Create a test campaign data object. * Create a test campaign data object.

View File

@ -24,9 +24,11 @@ import {
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { CardHeaderTitle } from '~/marketing/components'; import {
import { useCampaigns } from './useCampaigns'; CardHeaderTitle,
import { CreateNewCampaignModal } from './CreateNewCampaignModal'; CreateNewCampaignModal,
} from '~/marketing/components';
import { useCampaigns } from '~/marketing/hooks';
import './Campaigns.scss'; import './Campaigns.scss';
const tableCaption = __( 'Campaigns', 'woocommerce' ); const tableCaption = __( 'Campaigns', 'woocommerce' );

View File

@ -1,7 +1,13 @@
/** /**
* External dependencies * External dependencies
*/ */
import { Fragment, useState } from '@wordpress/element'; import {
Fragment,
useState,
forwardRef,
useImperativeHandle,
useRef,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { import {
Card, Card,
@ -32,11 +38,19 @@ type ChannelsProps = {
onInstalledAndActivated?: () => void; onInstalledAndActivated?: () => void;
}; };
export const Channels: React.FC< ChannelsProps > = ( { export type ChannelsRef = {
registeredChannels, /**
recommendedChannels, * Scroll into the "Add channels" section in the card.
onInstalledAndActivated, * The section will be expanded, and the "Add channels" button will be in focus.
} ) => { */
scrollIntoAddChannels: () => void;
};
export const Channels = forwardRef< ChannelsRef, ChannelsProps >(
(
{ registeredChannels, recommendedChannels, onInstalledAndActivated },
ref
) => {
const hasRegisteredChannels = registeredChannels.length >= 1; const hasRegisteredChannels = registeredChannels.length >= 1;
/** /**
@ -44,6 +58,21 @@ export const Channels: React.FC< ChannelsProps > = ( {
* Initial state is expanded if there are no registered channels in first page load. * Initial state is expanded if there are no registered channels in first page load.
*/ */
const [ expanded, setExpanded ] = useState( ! hasRegisteredChannels ); const [ expanded, setExpanded ] = useState( ! hasRegisteredChannels );
const addChannelsButtonRef = useRef< HTMLButtonElement >( null );
useImperativeHandle(
ref,
() => ( {
scrollIntoAddChannels: () => {
setExpanded( true );
addChannelsButtonRef.current?.focus();
addChannelsButtonRef.current?.scrollIntoView( {
block: 'center',
} );
},
} ),
[]
);
return ( return (
<Card className="woocommerce-marketing-channels-card"> <Card className="woocommerce-marketing-channels-card">
@ -62,16 +91,14 @@ export const Channels: React.FC< ChannelsProps > = ( {
</CardHeader> </CardHeader>
{ /* Registered channels section. */ } { /* Registered channels section. */ }
{ registeredChannels.map( ( el, idx ) => { { registeredChannels.map( ( el, idx ) => (
return (
<Fragment key={ el.slug }> <Fragment key={ el.slug }>
<RegisteredChannelCardBody registeredChannel={ el } /> <RegisteredChannelCardBody registeredChannel={ el } />
{ idx !== registeredChannels.length - 1 && ( { idx !== registeredChannels.length - 1 && (
<CardDivider /> <CardDivider />
) } ) }
</Fragment> </Fragment>
); ) ) }
} ) }
{ /* Recommended channels section. */ } { /* Recommended channels section. */ }
{ recommendedChannels.length >= 1 && ( { recommendedChannels.length >= 1 && (
@ -81,13 +108,18 @@ export const Channels: React.FC< ChannelsProps > = ( {
<CardDivider /> <CardDivider />
<CardBody> <CardBody>
<Button <Button
ref={ addChannelsButtonRef }
variant="link" variant="link"
onClick={ () => setExpanded( ! expanded ) } onClick={ () =>
setExpanded( ! expanded )
}
> >
{ __( 'Add channels', 'woocommerce' ) } { __( 'Add channels', 'woocommerce' ) }
<Icon <Icon
icon={ icon={
expanded ? chevronUp : chevronDown expanded
? chevronUp
: chevronDown
} }
size={ 24 } size={ 24 }
/> />
@ -96,8 +128,7 @@ export const Channels: React.FC< ChannelsProps > = ( {
</> </>
) } ) }
{ !! expanded && { !! expanded &&
recommendedChannels.map( ( el, idx ) => { recommendedChannels.map( ( el, idx ) => (
return (
<Fragment key={ el.plugin }> <Fragment key={ el.plugin }>
<SmartPluginCardBody <SmartPluginCardBody
plugin={ el } plugin={ el }
@ -110,10 +141,10 @@ export const Channels: React.FC< ChannelsProps > = ( {
<CardDivider /> <CardDivider />
) } ) }
</Fragment> </Fragment>
); ) ) }
} ) }
</div> </div>
) } ) }
</Card> </Card>
); );
}; }
);

View File

@ -1 +1,2 @@
export { Channels } from './Channels'; export { Channels } from './Channels';
export type { ChannelsRef } from './Channels';

View File

@ -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%;
}
}
}

View File

@ -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 (
<Card className="woocommerce-marketing-introduction-banner">
<div className="woocommerce-marketing-introduction-banner-content">
<div className="woocommerce-marketing-introduction-banner-title">
{ __(
'Reach new customers and increase sales without leaving WooCommerce',
'woocommerce'
) }
</div>
<Flex
className="woocommerce-marketing-introduction-banner-features"
direction="column"
gap={ 1 }
expanded={ false }
>
<FlexItem>
<Flex>
<Icon icon={ trendingUp } />
<FlexBlock>
{ __(
'Reach customers on other sales channels',
'woocommerce'
) }
</FlexBlock>
</Flex>
</FlexItem>
<FlexItem>
<Flex>
<Icon icon={ megaphone } />
<FlexBlock>
{ __(
'Advertise with marketing campaigns',
'woocommerce'
) }
</FlexBlock>
</Flex>
</FlexItem>
<FlexItem>
<Flex>
<img
src={ wooIconUrl }
alt={ __( 'WooCommerce logo', 'woocommerce' ) }
width="24"
height="24"
/>
<FlexBlock>
{ __( 'Built by WooCommerce', 'woocommerce' ) }
</FlexBlock>
</Flex>
</FlexItem>
</Flex>
{ ( showCreateCampaignButton || showAddChannelsButton ) && (
<Flex
className="woocommerce-marketing-introduction-banner-buttons"
justify="flex-start"
>
{ showCreateCampaignButton && (
<Button
variant="primary"
onClick={ () => {
setModalOpen( true );
} }
>
{ __( 'Create a campaign', 'woocommerce' ) }
</Button>
) }
{ showAddChannelsButton && (
<Button
variant="secondary"
onClick={ onAddChannelsClick }
>
{ __( 'Add channels', 'woocommerce' ) }
</Button>
) }
</Flex>
) }
{ isModalOpen && (
<CreateNewCampaignModal
onRequestClose={ () => setModalOpen( false ) }
/>
) }
</div>
<div className="woocommerce-marketing-introduction-banner-illustration">
<Button
isSmall
className="woocommerce-marketing-introduction-banner-close-button"
onClick={ onDismissClick }
>
<Icon icon={ closeSmall } />
</Button>
<div
className="woocommerce-marketing-introduction-banner-image-placeholder"
style={ {
backgroundImage: `url("${ illustrationUrl }")`,
} }
/>
</div>
</Card>
);
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1 @@
export { IntroductionBanner } from './IntroductionBanner';

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg preserveAspectRatio="xMidYMid" version="1.1" viewBox="0 0 256 153" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>WooCommerce Logo</title>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m23.759 0h208.38c13.187 0 23.863 10.675 23.863 23.863v79.542c0 13.187-10.675 23.863-23.863 23.863h-74.727l10.257 25.118-45.109-25.118h-98.695c-13.187 0-23.863-10.675-23.863-23.863v-79.542c-0.10466-13.083 10.571-23.863 23.758-23.863z" fill="#7f54b3"/>
<path d="m14.578 21.75c1.4569-1.9772 3.6423-3.0179 6.5561-3.226 5.3073-0.41626 8.3252 2.0813 9.0537 7.4927 3.226 21.75 6.7642 40.169 10.511 55.259l22.79-43.395c2.0813-3.9545 4.6829-6.0358 7.8049-6.2439 4.5789-0.3122 7.3886 2.6016 8.5333 8.7415 2.6016 13.841 5.9317 25.6 9.8862 35.59 2.7057-26.433 7.2846-45.476 13.737-57.236 1.561-2.9138 3.8504-4.3707 6.8683-4.5789 2.3935-0.20813 4.5789 0.52033 6.5561 2.0813 1.9772 1.561 3.0179 3.5382 3.226 5.9317 0.10406 1.8732-0.20813 3.4341-1.0407 4.9951-4.0585 7.4927-7.3886 20.085-10.094 37.567-2.6016 16.963-3.5382 30.179-2.9138 39.649 0.20813 2.6016-0.20813 4.8911-1.2488 6.8683-1.2488 2.2894-3.122 3.5382-5.5154 3.7463-2.7057 0.20813-5.5154-1.0406-8.2211-3.8504-9.678-9.8862-17.379-24.663-22.998-44.332-6.7642 13.32-11.759 23.311-14.985 29.971-6.1398 11.759-11.343 17.795-15.714 18.107-2.8098 0.20813-5.2033-2.1854-7.2846-7.1805-5.3073-13.633-11.031-39.961-17.171-78.985-0.41626-2.7057 0.20813-5.0992 1.665-6.9724zm223.64 16.338c-3.7463-6.5561-9.2618-10.511-16.65-12.072-1.9772-0.41626-3.8504-0.62439-5.6195-0.62439-9.9902 0-18.107 5.2033-24.455 15.61-5.4114 8.8455-8.1171 18.628-8.1171 29.346 0 8.013 1.665 14.881 4.9951 20.605 3.7463 6.5561 9.2618 10.511 16.65 12.072 1.9772 0.41626 3.8504 0.62439 5.6195 0.62439 10.094 0 18.211-5.2033 24.455-15.61 5.4114-8.9496 8.1171-18.732 8.1171-29.45 0.10406-8.1171-1.665-14.881-4.9951-20.501zm-13.112 28.826c-1.4569 6.8683-4.0585 11.967-7.9089 15.402-3.0179 2.7057-5.8276 3.8504-8.4293 3.3301-2.4976-0.52033-4.5789-2.7057-6.1398-6.7642-1.2488-3.226-1.8732-6.452-1.8732-9.4699 0-2.6016 0.20813-5.2033 0.72846-7.5967 0.93659-4.2667 2.7057-8.4293 5.5154-12.384 3.4341-5.0992 7.0764-7.1805 10.823-6.452 2.4976 0.52033 4.5789 2.7057 6.1398 6.7642 1.2488 3.226 1.8732 6.452 1.8732 9.4699 0 2.7057-0.20813 5.3073-0.72846 7.7008zm-52.033-28.826c-3.7463-6.5561-9.3659-10.511-16.65-12.072-1.9772-0.41626-3.8504-0.62439-5.6195-0.62439-9.9902 0-18.107 5.2033-24.455 15.61-5.4114 8.8455-8.1171 18.628-8.1171 29.346 0 8.013 1.665 14.881 4.9951 20.605 3.7463 6.5561 9.2618 10.511 16.65 12.072 1.9772 0.41626 3.8504 0.62439 5.6195 0.62439 10.094 0 18.211-5.2033 24.455-15.61 5.4114-8.9496 8.1171-18.732 8.1171-29.45 0-8.1171-1.665-14.881-4.9951-20.501zm-13.216 28.826c-1.4569 6.8683-4.0585 11.967-7.9089 15.402-3.0179 2.7057-5.8276 3.8504-8.4293 3.3301-2.4976-0.52033-4.5789-2.7057-6.1398-6.7642-1.2488-3.226-1.8732-6.452-1.8732-9.4699 0-2.6016 0.20813-5.2033 0.72846-7.5967 0.93658-4.2667 2.7057-8.4293 5.5154-12.384 3.4341-5.0992 7.0764-7.1805 10.823-6.452 2.4976 0.52033 4.5789 2.7057 6.1398 6.7642 1.2488 3.226 1.8732 6.452 1.8732 9.4699 0.10406 2.7057-0.20813 5.3073-0.72846 7.7008z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,6 +1,7 @@
/** /**
* External dependencies * External dependencies
*/ */
import { useRef } from '@wordpress/element';
import { useUser } from '@woocommerce/data'; import { useUser } from '@woocommerce/data';
/** /**
@ -10,19 +11,28 @@ import '~/marketing/data';
import '~/marketing/data-multichannel'; import '~/marketing/data-multichannel';
import { CenteredSpinner } from '~/marketing/components'; import { CenteredSpinner } from '~/marketing/components';
import { import {
useIntroductionBanner,
useCampaigns,
useRegisteredChannels, useRegisteredChannels,
useRecommendedChannels, useRecommendedChannels,
useCampaignTypes, useCampaignTypes,
} from '~/marketing/hooks'; } from '~/marketing/hooks';
import { getAdminSetting } from '~/utils/admin-settings'; import { getAdminSetting } from '~/utils/admin-settings';
import { IntroductionBanner } from './IntroductionBanner';
import { Campaigns } from './Campaigns'; import { Campaigns } from './Campaigns';
import { Channels } from './Channels'; import { Channels, ChannelsRef } from './Channels';
import { InstalledExtensions } from './InstalledExtensions'; import { InstalledExtensions } from './InstalledExtensions';
import { DiscoverTools } from './DiscoverTools'; import { DiscoverTools } from './DiscoverTools';
import { LearnMarketing } from './LearnMarketing'; import { LearnMarketing } from './LearnMarketing';
import './MarketingOverviewMultichannel.scss'; import './MarketingOverviewMultichannel.scss';
export const MarketingOverviewMultichannel: React.FC = () => { export const MarketingOverviewMultichannel: React.FC = () => {
const {
loading: loadingIntroductionBanner,
isIntroductionBannerDismissed,
dismissIntroductionBanner,
} = useIntroductionBanner();
const { loading: loadingCampaigns, meta: metaCampaigns } = useCampaigns();
const { const {
loading: loadingCampaignTypes, loading: loadingCampaignTypes,
data: dataCampaignTypes, data: dataCampaignTypes,
@ -36,8 +46,11 @@ export const MarketingOverviewMultichannel: React.FC = () => {
const { loading: loadingRecommended, data: dataRecommended } = const { loading: loadingRecommended, data: dataRecommended } =
useRecommendedChannels(); useRecommendedChannels();
const { currentUserCan } = useUser(); const { currentUserCan } = useUser();
const channelsRef = useRef< ChannelsRef >( null );
if ( if (
loadingIntroductionBanner ||
( loadingCampaigns && metaCampaigns?.total === undefined ) ||
( loadingCampaignTypes && ! dataCampaignTypes ) || ( loadingCampaignTypes && ! dataCampaignTypes ) ||
( loadingRegistered && ! dataRegistered ) || ( loadingRegistered && ! dataRegistered ) ||
( loadingRecommended && ! dataRecommended ) ( loadingRecommended && ! dataRecommended )
@ -45,6 +58,11 @@ export const MarketingOverviewMultichannel: React.FC = () => {
return <CenteredSpinner />; return <CenteredSpinner />;
} }
const shouldShowCampaigns = !! (
dataRegistered?.length &&
( isIntroductionBannerDismissed || metaCampaigns?.total )
);
const shouldShowExtensions = const shouldShowExtensions =
getAdminSetting( 'allowMarketplaceSuggestions', false ) && getAdminSetting( 'allowMarketplaceSuggestions', false ) &&
currentUserCan( 'install_plugins' ); currentUserCan( 'install_plugins' );
@ -56,10 +74,19 @@ export const MarketingOverviewMultichannel: React.FC = () => {
return ( return (
<div className="woocommerce-marketing-overview-multichannel"> <div className="woocommerce-marketing-overview-multichannel">
{ !! dataRegistered?.length && <Campaigns /> } { ! isIntroductionBannerDismissed && (
<IntroductionBanner
onDismissClick={ dismissIntroductionBanner }
onAddChannelsClick={ () => {
channelsRef.current?.scrollIntoAddChannels();
} }
/>
) }
{ shouldShowCampaigns && <Campaigns /> }
{ !! ( dataRegistered && dataRecommended ) && { !! ( dataRegistered && dataRecommended ) &&
!! ( dataRegistered.length || dataRecommended.length ) && ( !! ( dataRegistered.length || dataRecommended.length ) && (
<Channels <Channels
ref={ channelsRef }
registeredChannels={ dataRegistered } registeredChannels={ dataRegistered }
recommendedChannels={ dataRecommended } recommendedChannels={ dataRecommended }
onInstalledAndActivated={ refetch } onInstalledAndActivated={ refetch }

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add introduction banner to multichannel marketing page.