Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup Paid Ads Campaign Step 1 Account Connection #301

Merged
merged 23 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9093085
Add link to setup paid ads campaign page.
ecgan Feb 1, 2021
cb5c142
Add TopBar for Setup Ads flow.
ecgan Feb 2, 2021
5aa1ede
Code refactor TopBar component to accept title props.
ecgan Mar 8, 2021
d9683a8
Use TopBar component in SetupAds.
ecgan Mar 8, 2021
a080f21
Add AdsStepper.
ecgan Mar 8, 2021
bd7b1d5
Merge branch 'trunk' into feature/setup-ads-step-1
ecgan Mar 8, 2021
e893db7
Display GoogleAccount in AdsStepper.
ecgan Mar 8, 2021
89e987a
Use p element for better layout spacing.
ecgan Mar 8, 2021
113c0fd
Display GoogleAdsAccountSection.
ecgan Mar 8, 2021
e6765c1
Add audience section in CreateCampaign.
ecgan Mar 8, 2021
a11d10e
Code refactor with AccountSelectControl.
ecgan Mar 8, 2021
aab8275
Add wp-data for Google Ads account.
ecgan Mar 4, 2021
46c53c0
Add hooks for Google Ads account.
ecgan Mar 4, 2021
83afb7a
Move AccountId to shared components folder.
ecgan Mar 8, 2021
9a516b3
Move SpinnerCard to shared components folder.
ecgan Mar 8, 2021
ad75a8e
Create new Google Ads account or link an existing account.
ecgan Mar 8, 2021
dfabf69
Use import alias.
ecgan Mar 9, 2021
975b1ae
Fix termslink typo.
ecgan Mar 9, 2021
dd8ca71
Better handler name to avoid confusion with the onCreateAccount props.
ecgan Mar 9, 2021
1e33bf3
Display "Account " text in AccountSelectControl label.
ecgan Mar 9, 2021
de77b1d
Fix existing_ads missing in reducer default state.
ecgan Mar 9, 2021
14d69e2
Change "All Programs" label to "Programs" based on new design.
ecgan Mar 9, 2021
36191e4
Merge branch 'trunk' into feature/setup-ads-step-1
ecgan Mar 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions js/src/components/account-select-control/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AppSpinner from '.~/components/app-spinner';
import './index.scss';

const AccountSelectControl = ( props ) => {
const { accounts, ...rest } = props;

if ( ! accounts ) {
return <AppSpinner />;
}

const options = [
{
value: '',
label: __( 'Select one', 'google-listings-and-ads' ),
},
...accounts.map( ( acc ) => ( {
value: acc,
label: __( 'Account ', 'google-listings-and-ads' ) + acc,
} ) ),
];

return (
<div className="gla-account-select-control">
<SelectControl options={ options } { ...rest } />
</div>
);
};

export default AccountSelectControl;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.gla-merchant-center-select-control {
.gla-account-select-control {
.components-base-control__field {
margin-bottom: 0;
}
Expand Down
13 changes: 13 additions & 0 deletions js/src/components/ads-account-select-control/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Internal dependencies
*/
import useExistingGoogleAdsAccounts from '.~/hooks/useExistingGoogleAdsAccounts';
import AccountSelectControl from '../account-select-control';

const AdsAccountSelectControl = ( props ) => {
const { existingAccounts } = useExistingGoogleAdsAccounts();

return <AccountSelectControl accounts={ existingAccounts } { ...props } />;
};

export default AdsAccountSelectControl;
12 changes: 8 additions & 4 deletions js/src/components/google-account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ const GoogleAccount = ( props ) => {
<DisabledDiv disabled={ disabled }>
<Section
title={ __( 'Google account', 'google-listings-and-ads' ) }
description={ __(
'WooCommerce uses your Google account to sync with Google Merchant Center and Google Ads.',
'google-listings-and-ads'
) }
description={
<p>
{ __(
'WooCommerce uses your Google account to sync with Google Merchant Center and Google Ads.',
'google-listings-and-ads'
) }
</p>
}
>
<Section.Card>
<Section.Card.Body>
Expand Down
36 changes: 4 additions & 32 deletions js/src/components/merchant-center-select-control/index.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
/**
* External dependencies
*/
import { SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import useExistingGoogleMCAccounts from '.~/hooks/useExistingGoogleMCAccounts';
import AppSpinner from '.~/components/app-spinner';
import './index.scss';
import AccountSelectControl from '../account-select-control';

const MerchantCenterSelectControl = ( props ) => {
const { value, onChange } = props;
const { existingAccounts } = useExistingGoogleMCAccounts();

if ( ! existingAccounts ) {
return <AppSpinner />;
}

const options = [
{
value: '',
label: __( 'Select one', 'google-listings-and-ads' ),
},
...existingAccounts.map( ( acc ) => ( {
value: acc.id,
label: acc.id,
} ) ),
];
const accounts =
existingAccounts && existingAccounts.map( ( acc ) => acc.id );

return (
<div className="gla-merchant-center-select-control">
<SelectControl
options={ options }
value={ value }
onChange={ onChange }
/>
</div>
);
return <AccountSelectControl accounts={ accounts } { ...props } />;
};

export default MerchantCenterSelectControl;
2 changes: 1 addition & 1 deletion js/src/components/stepper/top-bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import './index.scss';
* to be used when configuring a campaign during oboarding and later.
*
* @param {Object} props
* @param {string} props.title Title to indcate where the user is at.
* @param {string} props.title Title to indicate where the user is at.
* @param {string} props.backHref Href for the back button.
* @param {Function} props.onBackButtonClick
* @param {Function} props.onHelpButtonClick
Expand Down
25 changes: 13 additions & 12 deletions js/src/dashboard/all-programs-table-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { getQuery, onQueryChange } from '@woocommerce/navigation';
import { getQuery, getNewPath, onQueryChange } from '@woocommerce/navigation';
import { Link } from '@woocommerce/components';
import classnames from 'classnames';

/**
* Internal dependencies
Expand Down Expand Up @@ -65,22 +66,22 @@ const AllProgramsTableCard = ( props ) => {
},
];

// TODO: what happens when click add paid campaign button.
const handleAddPaidCampaignClick = () => {};

return (
<AppTableCard
className="gla-all-programs-table-card"
title={
<div className="gla-all-programs-table-card__header">
{ __( 'All Programs', 'google-listings-and-ads' ) }
<Button
isSecondary
isSmall
onClick={ handleAddPaidCampaignClick }
{ __( 'Programs', 'google-listings-and-ads' ) }
<Link
className={ classnames(
'components-button',
'is-secondary',
'is-small'
) }
href={ getNewPath( {}, '/google/setup-ads' ) }
>
Add Paid Campaign
</Button>
{ __( 'Add Paid Campaign', 'google-listings-and-ads' ) }
</Link>
Copy link
Member

@tomalec tomalec Mar 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably it was not introduced here, but the placement of this link seems off from Figma:

Figma:
image

Actual:
image

BTW, "All programs" seem to be just "Programs" now, but this is a good candidate for a separate PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks fine for me, accessed from https://gla1.test/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fdashboard (I changed to "Programs", thanks for the heads up):

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, for me it looks different, and according to what I see in .woocommerce-table .components-card__header { /*...*/ grid-template-columns: auto 1fr auto;
, it should look that way
Screenshot from 2021-03-10 01-05-06

Anyway, it does look the same on trunk, so it's not the matter of this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomalec , thanks for sharing your screenshot. That is weird. Here's my screenshot, based on current trunk branch:

image

The thing is, within my "app-table-card" div, I have woocommerce-card but you have components-card. I look into the code in js/src/components/app-table-card/index.js, it should render WooCommerce Card. Do you have any local changes in your machine?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same results as Tomek. 👀

Copy link
Member

@eason9487 eason9487 Mar 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found some clues but have no idea how to fix that in the right way. When the WooCommerce Admin is activated in the Plugins page, it would load v1.9.0 of @woocommerce/components. Otherwise, it would load v1.8.3. And you can see they import different <Card> component sources. 😂

v1.9.0

https://github.com/woocommerce/woocommerce-admin/blob/v1.9.0/packages/components/src/table/index.js

import {
	Card,
// ...emit
} from '@wordpress/components';

v1.8.3

https://github.com/woocommerce/woocommerce-admin/blob/v1.8.3/packages/components/src/table/index.js

import Card from '../card';

@ecgan I believe once you activate the WooCommerce Admin plugin, you will see the same results. 😆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More precisely, the exactly loading version of @woocommerce/components should depend on the dependency of installed versions of the WooCommerce and WooCommerce Admin plugins.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eason9487 , thanks so much, that's awesome troubleshooting! 😃 👍

@ecgan I believe once you activate the WooCommerce Admin plugin, you will see the same results. 😆

I do have WC Admin activated, but it is version 1.8.0 😂

I created an issue to track this: #320

</div>
}
headers={ headers }
Expand Down
3 changes: 3 additions & 0 deletions js/src/data/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const TYPES = {
RECEIVE_ACCOUNTS_GOOGLE: 'RECEIVE_ACCOUNTS_GOOGLE',
RECEIVE_ACCOUNTS_GOOGLE_MC: 'RECEIVE_ACCOUNTS_GOOGLE_MC',
RECEIVE_ACCOUNTS_GOOGLE_MC_EXISTING: 'RECEIVE_ACCOUNTS_GOOGLE_MC_EXISTING',
RECEIVE_ACCOUNTS_GOOGLE_ADS: 'RECEIVE_ACCOUNTS_GOOGLE_ADS',
RECEIVE_ACCOUNTS_GOOGLE_ADS_EXISTING:
'RECEIVE_ACCOUNTS_GOOGLE_ADS_EXISTING',
RECEIVE_COUNTRIES: 'RECEIVE_COUNTRIES',
RECEIVE_TARGET_AUDIENCE: 'RECEIVE_TARGET_AUDIENCE',
SAVE_TARGET_AUDIENCE: 'SAVE_TARGET_AUDIENCE',
Expand Down
49 changes: 49 additions & 0 deletions js/src/data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,48 @@ export function* fetchExistingGoogleMCAccounts() {
}
}

export function* fetchGoogleAdsAccount() {
try {
const response = yield apiFetch( {
path: `${ API_NAMESPACE }/ads/connection`,
} );

return {
type: TYPES.RECEIVE_ACCOUNTS_GOOGLE_ADS,
account: response,
};
} catch ( error ) {
yield handleFetchError(
error,
__(
'There was an error loading Google Ads account info.',
'google-listings-and-ads'
)
);
}
}

export function* fetchExistingGoogleAdsAccounts() {
try {
const response = yield apiFetch( {
path: `${ API_NAMESPACE }/ads/accounts`,
} );

return {
type: TYPES.RECEIVE_ACCOUNTS_GOOGLE_ADS_EXISTING,
accounts: response,
};
} catch ( error ) {
yield handleFetchError(
error,
__(
'There was an error getting your Google Ads accounts.',
'google-listings-and-ads'
)
);
}
}

export function* fetchCountries() {
try {
const response = yield apiFetch( {
Expand Down Expand Up @@ -354,6 +396,13 @@ export function receiveMCAccount( account ) {
};
}

export function receiveAdsAccount( account ) {
return {
type: TYPES.RECEIVE_ACCOUNTS_GOOGLE_ADS,
account,
};
}

export function* saveTargetAudience( targetAudience ) {
try {
yield apiFetch( {
Expand Down
16 changes: 16 additions & 0 deletions js/src/data/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const DEFAULT_STATE = {
jetpack: null,
google: null,
mc: null,
ads: null,
existing_mc: null,
existing_ads: null,
},
},
};
Expand Down Expand Up @@ -128,6 +130,20 @@ const reducer = ( state = DEFAULT_STATE, action ) => {
return newState;
}

case TYPES.RECEIVE_ACCOUNTS_GOOGLE_ADS: {
const { account } = action;
const newState = cloneDeep( state );
newState.mc.accounts.ads = account;
return newState;
}

case TYPES.RECEIVE_ACCOUNTS_GOOGLE_ADS_EXISTING: {
const { accounts } = action;
const newState = cloneDeep( state );
newState.mc.accounts.existing_ads = accounts;
ecgan marked this conversation as resolved.
Show resolved Hide resolved
return newState;
}

case TYPES.RECEIVE_COUNTRIES: {
const { countries } = action;
const newState = cloneDeep( state );
Expand Down
10 changes: 10 additions & 0 deletions js/src/data/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
fetchGoogleAccount,
fetchGoogleMCAccount,
fetchExistingGoogleMCAccounts,
fetchGoogleAdsAccount,
fetchExistingGoogleAdsAccounts,
fetchCountries,
fetchTargetAudience,
} from './actions';
Expand Down Expand Up @@ -41,6 +43,14 @@ export function* getExistingGoogleMCAccounts() {
yield fetchExistingGoogleMCAccounts();
}

export function* getGoogleAdsAccount() {
yield fetchGoogleAdsAccount();
}

export function* getExistingGoogleAdsAccounts() {
yield fetchExistingGoogleAdsAccounts();
}

export function* getCountries() {
yield fetchCountries();
}
Expand Down
8 changes: 8 additions & 0 deletions js/src/data/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export const getExistingGoogleMCAccounts = ( state ) => {
return state.mc.accounts.existing_mc;
};

export const getGoogleAdsAccount = ( state ) => {
return state.mc.accounts.ads;
};

export const getExistingGoogleAdsAccounts = ( state ) => {
return state.mc.accounts.existing_ads;
};

export const getCountries = ( state ) => {
return state.mc.countries;
};
Expand Down
24 changes: 24 additions & 0 deletions js/src/hooks/useExistingGoogleAdsAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { STORE_KEY } from '.~/data/constants';

const useExistingGoogleAdsAccounts = () => {
return useSelect( ( select ) => {
const existingAccounts = select(
STORE_KEY
).getExistingGoogleAdsAccounts();
const isResolving = select( STORE_KEY ).isResolving(
'getExistingGoogleAdsAccounts'
);

return { existingAccounts, isResolving };
} );
};

export default useExistingGoogleAdsAccounts;
35 changes: 35 additions & 0 deletions js/src/hooks/useGoogleAdsAccount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { STORE_KEY } from '.~/data/constants';
import useGoogleAccount from './useGoogleAccount';

const useGoogleAdsAccount = () => {
const { google, isResolving } = useGoogleAccount();

return useSelect( ( select ) => {
if ( ! google || google.active === 'no' ) {
return {
googleAdsAccount: undefined,
isResolving,
};
}

const acc = select( STORE_KEY ).getGoogleAdsAccount();
const isResolvingGoogleAdsAccount = select( STORE_KEY ).isResolving(
'getGoogleAdsAccount'
);

return {
googleAdsAccount: acc,
isResolving: isResolvingGoogleAdsAccount,
};
} );
};

export default useGoogleAdsAccount;
Loading