-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Storage Add-ons: Expose add-on upsells to plans page #83005
Changes from all commits
eac824e
830b39b
12b1098
06edc32
a7f2a2c
4d6366e
2c06505
5ecc05f
0bfe4b6
e5662fa
17de485
5cd9bf5
7306cc5
8ff5ed5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ import page from 'page'; | |
import { useSelector } from 'react-redux'; | ||
import QueryActivePromotions from 'calypso/components/data/query-active-promotions'; | ||
import QueryPlans from 'calypso/components/data/query-plans'; | ||
import QueryProductsList from 'calypso/components/data/query-products-list'; | ||
import QuerySitePlans from 'calypso/components/data/query-site-plans'; | ||
import QuerySites from 'calypso/components/data/query-sites'; | ||
import FormattedHeader from 'calypso/components/formatted-header'; | ||
|
@@ -352,9 +353,14 @@ const PlansFeaturesMain = ( { | |
const planPath = cartItemForPlan?.product_slug | ||
? getPlanPath( cartItemForPlan.product_slug ) | ||
: ''; | ||
|
||
const checkoutUrl = cartItemForStorageAddOn | ||
? `/checkout/${ siteSlug }/${ planPath },${ cartItemForStorageAddOn.product_slug }:-q-${ cartItemForStorageAddOn.quantity }` | ||
: `/checkout/${ siteSlug }/${ planPath }`; | ||
|
||
const checkoutUrlWithArgs = addQueryArgs( | ||
{ ...( withDiscount && { coupon: withDiscount } ) }, | ||
`/checkout/${ siteSlug }/${ planPath }` | ||
checkoutUrl | ||
); | ||
|
||
page( checkoutUrlWithArgs ); | ||
|
@@ -651,6 +657,7 @@ const PlansFeaturesMain = ( { | |
<QuerySites siteId={ siteId } /> | ||
<QuerySitePlans siteId={ siteId } /> | ||
<QueryActivePromotions /> | ||
<QueryProductsList /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note:
|
||
<PlanUpsellModal | ||
isModalOpen={ isModalOpen } | ||
paidDomainName={ paidDomainName } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,11 +9,14 @@ import { | |
TERM_ANNUALLY, | ||
type PlanSlug, | ||
PLAN_HOSTING_TRIAL_MONTHLY, | ||
type StorageOption, | ||
} from '@automattic/calypso-products'; | ||
import { Button, Gridicon } from '@automattic/components'; | ||
import { WpcomPlansUI } from '@automattic/data-stores'; | ||
import { formatCurrency } from '@automattic/format-currency'; | ||
import { isMobile } from '@automattic/viewport'; | ||
import styled from '@emotion/styled'; | ||
import { useSelect } from '@wordpress/data'; | ||
import { useCallback } from '@wordpress/element'; | ||
import classNames from 'classnames'; | ||
import { localize, TranslateResult, useTranslate } from 'i18n-calypso'; | ||
|
@@ -23,6 +26,7 @@ import { useManageTooltipToggle } from 'calypso/my-sites/plans-grid/hooks/use-ma | |
import { useSelector } from 'calypso/state'; | ||
import { getPlanBillPeriod } from 'calypso/state/plans/selectors'; | ||
import { usePlansGridContext } from '../grid-context'; | ||
import useDefaultStorageOption from '../hooks/npm-ready/data-store/use-default-storage-option'; | ||
import { Plans2023Tooltip } from './plans-2023-tooltip'; | ||
import type { PlanActionOverrides } from '../types'; | ||
|
||
|
@@ -36,6 +40,7 @@ type PlanFeaturesActionsButtonProps = { | |
isPopular?: boolean; | ||
isInSignup?: boolean; | ||
isLaunchPage?: boolean | null; | ||
isMonthlyPlan?: boolean; | ||
onUpgradeClick: ( overridePlanSlug?: PlanSlug ) => void; | ||
planSlug: PlanSlug; | ||
flowName?: string | null; | ||
|
@@ -47,6 +52,7 @@ type PlanFeaturesActionsButtonProps = { | |
siteId?: number | null; | ||
isStuck: boolean; | ||
isLargeCurrency?: boolean; | ||
storageOptions?: StorageOption[]; | ||
}; | ||
|
||
const DummyDisabledButton = styled.div` | ||
|
@@ -208,6 +214,7 @@ const LoggedInPlansFeatureActionButton = ( { | |
priceString, | ||
isStuck, | ||
isLargeCurrency, | ||
isMonthlyPlan, | ||
planTitle, | ||
handleUpgradeButtonClick, | ||
planSlug, | ||
|
@@ -216,34 +223,56 @@ const LoggedInPlansFeatureActionButton = ( { | |
currentSitePlanSlug, | ||
buttonText, | ||
planActionOverrides, | ||
storageOptions, | ||
}: { | ||
freePlan: boolean; | ||
availableForPurchase?: boolean; | ||
classes: string; | ||
priceString: string | null; | ||
isStuck: boolean; | ||
isLargeCurrency: boolean; | ||
isMonthlyPlan?: boolean; | ||
planTitle: TranslateResult; | ||
handleUpgradeButtonClick: () => void; | ||
planSlug: string; | ||
planSlug: PlanSlug; | ||
currentPlanManageHref?: string; | ||
canUserManageCurrentPlan?: boolean | null; | ||
currentSitePlanSlug?: string | null; | ||
buttonText?: string; | ||
planActionOverrides?: PlanActionOverrides; | ||
storageOptions?: StorageOption[]; | ||
} ) => { | ||
const [ activeTooltipId, setActiveTooltipId ] = useManageTooltipToggle(); | ||
const translate = useTranslate(); | ||
const { gridPlansIndex } = usePlansGridContext(); | ||
const { current } = gridPlansIndex[ planSlug ]; | ||
const selectedStorageOptionForPlan = useSelect( | ||
( select ) => select( WpcomPlansUI.store ).getSelectedStorageOptionForPlan( planSlug ), | ||
[ planSlug ] | ||
); | ||
const { current, storageAddOnsForPlan } = gridPlansIndex[ planSlug ]; | ||
const defaultStorageOption = useDefaultStorageOption( { | ||
storageOptions, | ||
storageAddOnsForPlan, | ||
} ); | ||
const canPurchaseStorageAddOns = storageAddOnsForPlan?.some( | ||
( storageAddOn ) => ! storageAddOn?.purchased && ! storageAddOn?.exceedsSiteStorageLimits | ||
); | ||
const storageAddOnCheckoutHref = storageAddOnsForPlan?.find( | ||
( addOn ) => | ||
selectedStorageOptionForPlan && addOn?.featureSlugs?.includes( selectedStorageOptionForPlan ) | ||
)?.checkoutLink; | ||
const nonDefaultStorageOptionSelected = defaultStorageOption !== selectedStorageOptionForPlan; | ||
const currentPlanBillPeriod = useSelector( ( state ) => { | ||
return currentSitePlanSlug ? getPlanBillPeriod( state, currentSitePlanSlug ) : null; | ||
} ); | ||
const gridPlanBillPeriod = useSelector( ( state ) => { | ||
return planSlug ? getPlanBillPeriod( state, planSlug ) : null; | ||
} ); | ||
|
||
if ( freePlan ) { | ||
if ( | ||
freePlan || | ||
( storageAddOnsForPlan && ! canPurchaseStorageAddOns && nonDefaultStorageOptionSelected ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a thought. Since we use this condition ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey apologies if I'm not seeing it, but one of the conditions is I'm not seeing how they can be combined 😅 |
||
) { | ||
if ( planActionOverrides?.loggedInFreePlan ) { | ||
return ( | ||
<Button | ||
|
@@ -264,6 +293,17 @@ const LoggedInPlansFeatureActionButton = ( { | |
} | ||
|
||
if ( current && planSlug !== PLAN_P2_FREE ) { | ||
if ( canPurchaseStorageAddOns && nonDefaultStorageOptionSelected && ! isMonthlyPlan ) { | ||
return ( | ||
<Button | ||
className={ classNames( classes, 'is-storage-upgradeable' ) } | ||
href={ storageAddOnCheckoutHref } | ||
> | ||
{ translate( 'Upgrade' ) } | ||
</Button> | ||
); | ||
} | ||
|
||
return ( | ||
<Button | ||
className={ classes } | ||
|
@@ -341,10 +381,16 @@ const LoggedInPlansFeatureActionButton = ( { | |
comment: '%(priceString)s is the full price including the currency. Eg: Get Upgrade - $10', | ||
} ); | ||
} else if ( isStuck && isLargeCurrency ) { | ||
buttonTextFallback = translate( 'Upgrade – %(plan)s', { | ||
context: 'verb', | ||
args: { plan: planTitle ?? '' }, | ||
comment: '%(plan)s is the name of the plan ', | ||
buttonTextFallback = translate( 'Get %(plan)s {{span}}%(priceString)s{{/span}}', { | ||
args: { | ||
plan: planTitle, | ||
priceString: priceString ?? '', | ||
}, | ||
comment: | ||
'%(plan)s is the name of the plan and %(priceString)s is the full price including the currency. Eg: Get Premium - $10', | ||
components: { | ||
span: <span className="plan-features-2023-grid__actions-signup-plan-text" />, | ||
}, | ||
} ); | ||
} else { | ||
buttonTextFallback = translate( 'Upgrade', { context: 'verb' } ); | ||
|
@@ -398,6 +444,8 @@ const PlanFeaturesActionsButton: React.FC< PlanFeaturesActionsButtonProps > = ( | |
planActionOverrides, | ||
isStuck, | ||
isLargeCurrency, | ||
isMonthlyPlan, | ||
storageOptions, | ||
} ) => { | ||
const translate = useTranslate(); | ||
const { gridPlansIndex } = usePlansGridContext(); | ||
|
@@ -513,7 +561,9 @@ const PlanFeaturesActionsButton: React.FC< PlanFeaturesActionsButtonProps > = ( | |
priceString={ priceString } | ||
isStuck={ isStuck } | ||
isLargeCurrency={ !! isLargeCurrency } | ||
isMonthlyPlan={ isMonthlyPlan } | ||
planTitle={ planTitle } | ||
storageOptions={ storageOptions } | ||
/> | ||
); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only change made in this file is the usage of
useCallback
to align with the 2023 plans-grid optimization efforts p1697705360647249/1697700875.046909-slack-CV2TX2PANThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I don't think this needs to return a callback at all. We should probably instead be passing in#83005 (comment)selectedSiteSlug
and return the checkout link directly from the hook.