Skip to content

Commit 21a9dbb

Browse files
committed
A4A > Marketplace: Implement Term pricing for Hosting, Products & Cart
1 parent c8ad997 commit 21a9dbb

File tree

15 files changed

+269
-82
lines changed

15 files changed

+269
-82
lines changed

client/a8c-for-agencies/sections/marketplace/commissions-info/index.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1+
import { isEnabled } from '@automattic/calypso-config';
12
import { formatCurrency } from '@automattic/number-formatters';
23
import { useTranslate } from 'i18n-calypso';
34
import { getProductCommissionPercentage } from '../../referrals/lib/commissions';
4-
import type { ShoppingCartItem } from '../types';
5+
import type { ShoppingCartItem, TermPricingType } from '../types';
56

67
import './style.scss';
78

8-
export default function CommissionsInfo( { items }: { items: ShoppingCartItem[] } ) {
9+
export default function CommissionsInfo( {
10+
items,
11+
termPricing,
12+
}: {
13+
items: ShoppingCartItem[];
14+
termPricing?: TermPricingType;
15+
} ) {
916
const translate = useTranslate();
1017

18+
const isTermPricingEnabled = isEnabled( 'a4a-bd-term-pricing' ) && isEnabled( 'a4a-bd-checkout' );
19+
1120
const totalCommissions = items.reduce( ( acc, item ) => {
1221
const product = item;
1322
const commissionPercentage = getProductCommissionPercentage( product.family_slug );
@@ -22,15 +31,30 @@ export default function CommissionsInfo( { items }: { items: ShoppingCartItem[]
2231
return null;
2332
}
2433

34+
const totalPricePerTerm =
35+
termPricing === 'yearly'
36+
? translate( '%(total)s/yr', {
37+
args: {
38+
total: 'YEARLY PRICE',
39+
},
40+
} )
41+
: translate( '%(total)s/mo', {
42+
args: {
43+
total: 'MONTHLY PRICE',
44+
},
45+
} );
46+
2547
return (
2648
<div className="commissions-info">
2749
<span>{ translate( 'Your estimated commission:' ) }</span>
2850
<span>
29-
{ translate( '%(total)s/mo', {
30-
args: {
31-
total: formatCurrency( totalCommissions, 'USD' ),
32-
},
33-
} ) }
51+
{ isTermPricingEnabled
52+
? totalPricePerTerm
53+
: translate( '%(total)s/mo', {
54+
args: {
55+
total: formatCurrency( totalCommissions, 'USD' ),
56+
},
57+
} ) }
3458
</span>
3559
</div>
3660
);

client/a8c-for-agencies/sections/marketplace/hooks/use-total-invoice-value.ts

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { isEnabled } from '@automattic/calypso-config';
2+
import { formatCurrency } from '@automattic/number-formatters';
3+
import { useTranslate } from 'i18n-calypso';
14
import { useContext, useMemo } from 'react';
25
import useProductsQuery from 'calypso/a8c-for-agencies/data/marketplace/use-products-query';
36
import useWPCOMOwnedSites from 'calypso/a8c-for-agencies/hooks/use-wpcom-owned-sites';
@@ -8,8 +11,12 @@ import { SelectedLicenseProp } from 'calypso/jetpack-cloud/sections/partner-port
811
import { APIProductFamily, APIProductFamilyProduct } from 'calypso/state/partner-portal/types';
912
import { ProductListItem } from 'calypso/state/products-list/selectors/get-products-list';
1013
import { MarketplaceTypeContext } from '../context';
14+
import { TermPricingType } from '../types';
1115

12-
export const useGetProductPricingInfo = () => {
16+
const isTermPricingEnabled = isEnabled( 'a4a-bd-term-pricing' ) && isEnabled( 'a4a-bd-checkout' );
17+
18+
export const useGetProductPricingInfo = ( termPricing?: TermPricingType, currency?: string ) => {
19+
const translate = useTranslate();
1320
const { data } = useProductsQuery( false, true );
1421
const wpcomProducts = data?.find(
1522
( product ) => product.slug === 'wpcom-hosting'
@@ -41,6 +48,11 @@ export const useGetProductPricingInfo = () => {
4148
actualCost,
4249
discountedCost: actualCost * ( 1 - tier.discount ),
4350
discountPercentage: tier.discount,
51+
showActualCost: false,
52+
termPricingText: '',
53+
discountedCostFormatted: '',
54+
actualCostFormatted: '',
55+
isFree: false,
4456
};
4557
}
4658

@@ -105,20 +117,42 @@ export const useGetProductPricingInfo = () => {
105117
discountInfo.actualCost = actualCost;
106118
}
107119
}
108-
return discountInfo;
120+
121+
// TODO: Use the actual pricing text from the product
122+
const termBasedDiscountedCostFormatted =
123+
termPricing === 'yearly' ? 'YEARLY PRICE' : 'MONTHLY PRICE';
124+
125+
const termBasedActualCostFormatted =
126+
termPricing === 'yearly' ? 'YEARLY PRICE' : 'MONTHLY PRICE';
127+
128+
return {
129+
...discountInfo,
130+
discountedCostFormatted: isTermPricingEnabled
131+
? termBasedDiscountedCostFormatted
132+
: formatCurrency( discountInfo.discountedCost, currency ?? 'USD' ),
133+
actualCostFormatted: isTermPricingEnabled
134+
? termBasedActualCostFormatted
135+
: formatCurrency( discountInfo.actualCost, currency ?? 'USD' ),
136+
showActualCost: discountInfo.actualCost > discountInfo.discountedCost,
137+
termPricingText:
138+
isTermPricingEnabled && termPricing === 'yearly' ? translate( '/yr' ) : translate( '/mo' ),
139+
isFree: discountInfo.actualCost === 0,
140+
};
109141
};
110142
return { getProductPricingInfo };
111143
};
112144

113-
export const useTotalInvoiceValue = () => {
114-
const { getProductPricingInfo } = useGetProductPricingInfo();
145+
export const useTotalInvoiceValue = ( termPricing?: TermPricingType, currency?: string ) => {
146+
const translate = useTranslate();
147+
148+
const { getProductPricingInfo } = useGetProductPricingInfo( termPricing, currency );
115149

116150
const getTotalInvoiceValue = (
117151
userProducts: Record< string, ProductListItem >,
118152
selectedLicenses: SelectedLicenseProp[]
119153
) => {
120154
// Use the reduce function to calculate the total invoice value
121-
return selectedLicenses.reduce(
155+
const totalInvoiceValue = selectedLicenses.reduce(
122156
( acc, license ) => {
123157
// Get the pricing information for the current license
124158
const { actualCost, discountedCost, discountPercentage } = getProductPricingInfo(
@@ -139,6 +173,33 @@ export const useTotalInvoiceValue = () => {
139173
discountPercentage: 0,
140174
}
141175
);
176+
177+
// TODO: Use the actual pricing text from the product
178+
const termBasedTotalDiscountedCostFormatted =
179+
termPricing === 'yearly'
180+
? translate( '%(total)s/yr', {
181+
args: {
182+
total: 'YEARLY PRICE',
183+
},
184+
} )
185+
: translate( '%(total)s/mo', {
186+
args: {
187+
total: 'MONTHLY PRICE',
188+
},
189+
} );
190+
191+
const monthlyTotalDiscountedCostFormatted = translate( '%(total)s/mo', {
192+
args: {
193+
total: formatCurrency( totalInvoiceValue.discountedCost, currency ?? 'USD' ),
194+
},
195+
} );
196+
197+
return {
198+
...totalInvoiceValue,
199+
totalDiscountedCostFormatted: isTermPricingEnabled
200+
? termBasedTotalDiscountedCostFormatted
201+
: monthlyTotalDiscountedCostFormatted,
202+
};
142203
};
143204

144205
return { getTotalInvoiceValue };

client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-content/premier-agency-hosting/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { recordTracksEvent } from 'calypso/state/analytics/actions';
2020
import { APIProductFamilyProduct } from 'calypso/state/partner-portal/types';
2121
import HostingAdditionalFeaturesSection from '../../../common/hosting-additional-features-section';
2222
import HostingTestimonialsSection from '../../../common/hosting-testimonials-section';
23-
import { MarketplaceTypeContext } from '../../../context';
23+
import { MarketplaceTypeContext, TermPricingContext } from '../../../context';
2424
import useGetPressablePlanByProductId from '../../../pressable-overview/hooks/use-get-pressable-plan-by-product-id';
2525
import getPressablePlan from '../../../pressable-overview/lib/get-pressable-plan';
2626
import usePressableOwnershipType from '../../hooks/use-pressable-ownership-type';
@@ -44,6 +44,8 @@ export default function PremierAgencyHosting( { onAddToCart }: Props ) {
4444
};
4545

4646
const { marketplaceType } = useContext( MarketplaceTypeContext );
47+
const { termPricing } = useContext( TermPricingContext );
48+
4749
const pressableOwnership = usePressableOwnershipType();
4850

4951
const isReferralMode = marketplaceType === 'referral';
@@ -97,6 +99,7 @@ export default function PremierAgencyHosting( { onAddToCart }: Props ) {
9799
existingPlan={ agencyPressablePlan }
98100
existingPlanInfo={ existingPlanInfo }
99101
isFetching={ isExistingPlanFetched }
102+
termPricing={ termPricing }
100103
/>
101104

102105
<HostingFeatures

client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-content/premier-agency-hosting/pressable-plan-section/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import HostingPlanSection from '../../common/hosting-plan-section';
2222
import CustomPlanCardContent from './custom-plan-card-content';
2323
import PremiumPlanSection from './premium-plan-section';
2424
import RegularPlanCardContent from './regular-plan-card-content';
25+
import type { TermPricingType } from 'calypso/a8c-for-agencies/sections/marketplace/types';
2526

2627
import './style.scss';
2728

@@ -32,6 +33,7 @@ type Props = {
3233
existingPlan: APIProductFamilyProduct | null;
3334
existingPlanInfo: PressablePlan | null;
3435
isFetching?: boolean;
36+
termPricing: TermPricingType;
3537
};
3638

3739
const getSelectedTab = (
@@ -67,6 +69,7 @@ export default function PressablePlanSection( {
6769
existingPlan,
6870
existingPlanInfo,
6971
isFetching,
72+
termPricing,
7073
}: Props ) {
7174
const translate = useTranslate();
7275

@@ -210,6 +213,7 @@ export default function PressablePlanSection( {
210213
onSelect={ onPlanAddToCart }
211214
isReferralMode={ isReferralMode }
212215
pressableOwnership={ pressableOwnership }
216+
termPricing={ termPricing }
213217
/>
214218
) }
215219
</HostingPlanSection.Card>

client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-content/premier-agency-hosting/pressable-plan-section/regular-plan-card-content.tsx

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { formatCurrency } from '@automattic/number-formatters';
1+
import { isEnabled } from '@automattic/calypso-config';
22
import { Button } from '@wordpress/components';
33
import { useTranslate } from 'i18n-calypso';
44
import { useMemo } from 'react';
@@ -7,28 +7,31 @@ import PressableLogo from 'calypso/assets/images/a8c-for-agencies/pressable-logo
77
import { useSelector } from 'calypso/state';
88
import { APIProductFamilyProduct } from 'calypso/state/partner-portal/types';
99
import { getProductsList } from 'calypso/state/products-list/selectors';
10+
import type { TermPricingType } from 'calypso/a8c-for-agencies/sections/marketplace/types';
1011

1112
type Props = {
1213
plan: APIProductFamilyProduct;
1314
onSelect: ( plan: APIProductFamilyProduct ) => void;
1415
isReferralMode?: boolean;
1516
pressableOwnership?: 'agency' | 'regular' | 'none';
17+
termPricing: TermPricingType;
1618
};
1719

18-
export default function PressablePlanSelectorCard( {
20+
export default function RegularPlanCardContent( {
1921
plan,
2022
onSelect,
2123
isReferralMode,
2224
pressableOwnership,
25+
termPricing,
2326
}: Props ) {
2427
const translate = useTranslate();
2528
const userProducts = useSelector( getProductsList );
2629

27-
const { getProductPricingInfo } = useGetProductPricingInfo();
30+
const { getProductPricingInfo } = useGetProductPricingInfo( termPricing, plan.currency );
2831

29-
const { discountedCost } = plan
32+
const { discountedCostFormatted } = plan
3033
? getProductPricingInfo( userProducts, plan, 1 )
31-
: { discountedCost: 0 };
34+
: { discountedCostFormatted: '' };
3235

3336
const ctaLabel = useMemo( () => {
3437
if ( isReferralMode ) {
@@ -48,6 +51,21 @@ export default function PressablePlanSelectorCard( {
4851
} );
4952
}, [ isReferralMode, plan.name, translate ] );
5053

54+
const isTermPricingEnabled = isEnabled( 'a4a-bd-term-pricing' ) && isEnabled( 'a4a-bd-checkout' );
55+
56+
const priceInterval = () => {
57+
if ( isTermPricingEnabled ) {
58+
return termPricing === 'yearly' ? translate( 'per year' ) : translate( 'per month' );
59+
}
60+
if ( plan.price_interval === 'day' ) {
61+
return translate( 'per day, billed monthly' );
62+
}
63+
if ( plan.price_interval === 'month' ) {
64+
return translate( 'per month, billed monthly' );
65+
}
66+
return '';
67+
};
68+
5169
return (
5270
<div className="pressable-plan-card-content">
5371
<div className="pressable-plan-card-content__top">
@@ -65,13 +83,10 @@ export default function PressablePlanSelectorCard( {
6583
) : (
6684
<div className="pressable-plan-card-content__price">
6785
<b className="pressable-plan-card-content__price-actual-value">
68-
{ formatCurrency( discountedCost, plan.currency ) }
86+
{ discountedCostFormatted }
6987
</b>
7088

71-
<div className="pressable-plan-card-content__price-interval">
72-
{ plan.price_interval === 'day' && translate( 'per day, billed monthly' ) }
73-
{ plan.price_interval === 'month' && translate( 'per month, billed monthly' ) }
74-
</div>
89+
<div className="pressable-plan-card-content__price-interval">{ priceInterval() }</div>
7590
</div>
7691
) }
7792
</div>

client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-content/standard-agency-hosting/wpcom-plan-section/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { useRandomSiteName } from 'calypso/a8c-for-agencies/components/site-conf
88
import useFetchDevLicenses from 'calypso/a8c-for-agencies/data/purchases/use-fetch-dev-licenses';
99
import useSiteCreatedCallback from 'calypso/a8c-for-agencies/hooks/use-site-created-callback';
1010
import useWPCOMOwnedSites from 'calypso/a8c-for-agencies/hooks/use-wpcom-owned-sites';
11-
import { MarketplaceTypeContext } from 'calypso/a8c-for-agencies/sections/marketplace/context';
11+
import {
12+
MarketplaceTypeContext,
13+
TermPricingContext,
14+
} from 'calypso/a8c-for-agencies/sections/marketplace/context';
1215
import useProductAndPlans from 'calypso/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans';
1316
import { getWPCOMCreatorPlan } from 'calypso/a8c-for-agencies/sections/marketplace/lib/hosting';
1417
import { useDispatch } from 'calypso/state';
@@ -59,6 +62,8 @@ export default function WPCOMPlanSection( { onSelect }: Props ) {
5962
const { marketplaceType } = useContext( MarketplaceTypeContext );
6063
const isReferralMode = marketplaceType === 'referral';
6164

65+
const { termPricing } = useContext( TermPricingContext );
66+
6267
const ownedPlans = useMemo( () => {
6368
if ( isReferralMode ) {
6469
return 0;
@@ -107,6 +112,7 @@ export default function WPCOMPlanSection( { onSelect }: Props ) {
107112
isReferralMode={ isReferralMode }
108113
quantity={ displayQuantity }
109114
setQuantity={ setQuantity }
115+
termPricing={ termPricing }
110116
/>
111117
) : (
112118
<WPCOMPlanSelector.Placeholder />

0 commit comments

Comments
 (0)