Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions client/blocks/product-purchase-features-list/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
WPCOM_FEATURES_WORDADS,
TYPE_WOOEXPRESS_MEDIUM,
TYPE_WOOEXPRESS_SMALL,
TYPE_WOO_HOSTED_BASIC,
TYPE_WOO_HOSTED_PRO,
TYPE_100_YEAR,
TYPE_STARTER,
} from '@automattic/calypso-products';
Expand Down Expand Up @@ -395,6 +397,8 @@ export class ProductPurchaseFeaturesList extends Component {
[ TYPE_ECOMMERCE ]: () => this.getEcommerceFeatures(),
[ TYPE_WOOEXPRESS_MEDIUM ]: () => this.getEcommerceFeatures(),
[ TYPE_WOOEXPRESS_SMALL ]: () => this.getEcommerceFeatures(),
[ TYPE_WOO_HOSTED_BASIC ]: () => this.getEcommerceFeatures(),
[ TYPE_WOO_HOSTED_PRO ]: () => this.getEcommerceFeatures(),
[ TYPE_BUSINESS ]: () => this.getBusinessFeatures(),
[ TYPE_PREMIUM ]: () => this.getPremiumFeatures(),
[ TYPE_PERSONAL ]: () => this.getPersonalFeatures(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ function getUpgradeUrl( purchase: Purchase ): string | undefined {
return `/plans/${ purchase.site_slug }`;
}

if ( purchase.is_woo_hosted_product ) {
return `/setup/woo-hosted-plans?siteSlug=${ purchase.site_slug }`;
}

return getWpcomPlanGridUrl( purchase.site_slug );
}

Expand Down
2 changes: 1 addition & 1 deletion client/dashboard/utils/site-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function useSitePlanManageURL( site: Site, purchase?: Purchase ) {

if ( site.plan?.is_free ) {
return isCommerceGarden( site )
? `${ protocol }//${ host }/plans/${ site.slug }`
? `${ protocol }//${ host }/setup/woo-hosted-plans?siteSlug=${ site.slug }`
: `${ protocol }//${ host }/setup/plan-upgrade?siteSlug=${ site.slug }`;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# woo-hosted flow

## Testing instructions

Please improve the instructions on how to test this flow.

1. Go to /setup/woo-hosted-plans.

## Owned by

@michaeldcain

## Context

<https://wp.me/pgzWbf-ND>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.is-section-stepper .woo-hosted-plans {
.step-container-v2__top-bar-wordpress-logo-wrapper,
.step-container-v2__top-bar-divider {
display: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { WOO_HOSTED_PLANS_FLOW } from '@automattic/onboarding';
import { resolveSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';
import { STEPS } from 'calypso/landing/stepper/declarative-flow/internals/steps';
import { FlowV2, SubmitHandler } from 'calypso/landing/stepper/declarative-flow/internals/types';
import { useQuery } from 'calypso/landing/stepper/hooks/use-query';
import { SITE_STORE } from 'calypso/landing/stepper/stores';
import { getCurrentQueryParams } from 'calypso/landing/stepper/utils/get-current-query-params';
import { stepsWithRequiredLogin } from 'calypso/landing/stepper/utils/steps-with-required-login';
import { isExternal } from 'calypso/lib/url';
import './style.scss';

const BASE_STEPS = [ STEPS.UNIFIED_PLANS ];

/**
* Checks if the user has access to upgrade plans for the given site
*/
async function checkUserHasAccess(): Promise< boolean > {
// Get site slug or ID from query params
const queryParams = getCurrentQueryParams();
const siteSlugFromQuery = queryParams.get( 'siteSlug' );
const siteIdFromQuery = queryParams.get( 'siteId' );

const siteIdOrSlug = siteSlugFromQuery || siteIdFromQuery;

if ( ! siteIdOrSlug ) {
return false;
}

try {
const site = await resolveSelect( SITE_STORE ).getSite( siteIdOrSlug );

if ( ! site ) {
return false;
}

// Check if user can manage the site using the capabilities from the site object
return site.capabilities?.manage_options === true;
} catch ( error ) {
return false;
}
}

async function initialize() {
const hasAccess = await checkUserHasAccess();

if ( ! hasAccess ) {
window.location.assign( '/ciab/sites' );
return false;
}

return stepsWithRequiredLogin( BASE_STEPS );
}

const wooHostedPlansFlow: FlowV2< typeof initialize > = {
name: WOO_HOSTED_PLANS_FLOW,
title: __( 'Pick a plan for your store' ),
isSignupFlow: false,
__experimentalUseSessions: true,
__experimentalUseBuiltinAuth: true,
initialize,

useStepsProps() {
const query = useQuery();
const backTo = query.get( 'back_to' ) ?? query.get( 'cancel_to' ) ?? undefined;

// Validate back_to to prevent open redirect - must not be external
const safeBackTo = backTo && ! isExternal( backTo ) ? backTo : '/ciab/sites';

return {
[ STEPS.UNIFIED_PLANS.slug ]: {
// This flag enables upgrade-specific behavior in PlansFeaturesMain
isStepperUpgradeFlow: true,

// This is NOT a signup flow - use logged-in behavior for current plans
isInSignup: false,

// Provide a custom back handler that goes to back_to or /ciab/sites
wrapperProps: {
goBack: () => {
window.location.assign( safeBackTo );
},
},

// Woo Hosted only supports monthly and yearly plans
displayedIntervals: [ 'monthly', 'yearly' ],
},
};
},

useStepNavigation() {
const query = useQuery();
const siteSlug = query.get( 'siteSlug' );
const redirectTo = query.get( 'redirect_to' );

const submit: SubmitHandler< typeof initialize > = ( submittedStep ) => {
const { slug, providedDependencies } = submittedStep;

switch ( slug ) {
case STEPS.UNIFIED_PLANS.slug: {
// User selected plan, go directly to checkout
if ( providedDependencies?.cartItems && providedDependencies.cartItems.length > 0 ) {
const selectedPlan = providedDependencies.cartItems[ 0 ]?.product_slug;
if ( selectedPlan && siteSlug ) {
const checkoutUrl = `/checkout/${ encodeURIComponent( siteSlug ) }/${ selectedPlan }`;
const currentPath = window.location.href.replace( window.location.origin, '' );

// Build checkout URL with query params
// Note: Not using goToCheckout utility because it hardcodes signup=1
// Checkout validates redirect_to to prevent open redirects
const finalUrl = addQueryArgs( checkoutUrl, {
redirect_to: redirectTo || '/ciab/sites',
cancel_to: currentPath,
} );

window.location.assign( finalUrl );
}
return;
}

// If no cart items, something went wrong - redirect to sites
window.location.assign( '/sites' );
break;
}
}
};

return { submit };
},
};

export default wooHostedPlansFlow;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ONBOARDING_UNIFIED_FLOW,
DOMAIN_FLOW,
PLAN_UPGRADE_FLOW,
WOO_HOSTED_PLANS_FLOW,
} from '@automattic/onboarding';

const FLOWS_USING_STEP_CONTAINER_V2 = [
Expand All @@ -18,6 +19,7 @@ const FLOWS_USING_STEP_CONTAINER_V2 = [
ONBOARDING_UNIFIED_FLOW,
DOMAIN_FLOW,
PLAN_UPGRADE_FLOW,
WOO_HOSTED_PLANS_FLOW,
];

export const shouldUseStepContainerV2 = ( flow: string ) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ONBOARDING_UNIFIED_FLOW,
PLAN_UPGRADE_FLOW,
START_WRITING_FLOW,
WOO_HOSTED_PLANS_FLOW,
Step,
useStepPersistedState,
} from '@automattic/onboarding';
Expand All @@ -29,7 +30,7 @@ import { getTheme, getThemeType } from 'calypso/state/themes/selectors';
import { shouldUseStepContainerV2 } from '../../../helpers/should-use-step-container-v2';
import { playgroundPlansIntent } from '../playground/lib/plans';
import UnifiedPlansStep from './unified-plans-step';
import { getIntervalType, getVisualSplitPlansIntent } from './util';
import { getIntervalType, getVisualSplitPlansIntent, SupportedIntervalTypes } from './util';
import type { Step as StepType } from '../../types';
import type { PlansIntent } from '@automattic/plans-grid-next';
import type { MinimalRequestCartProduct } from '@automattic/shopping-cart';
Expand Down Expand Up @@ -73,6 +74,8 @@ function getPlansIntent( flowName: string | null ): PlansIntent | null {
return 'plans-affiliate';
case PLAN_UPGRADE_FLOW:
return 'plans-upgrade';
case WOO_HOSTED_PLANS_FLOW:
return 'plans-woo-hosted';
default:
return null;
}
Expand All @@ -90,6 +93,7 @@ const PlansStepAdaptor: StepType< {
isInSignup?: boolean;
isStepperUpgradeFlow?: boolean;
selectedFeature?: string;
displayedIntervals?: SupportedIntervalTypes[];
wrapperProps?: {
hideBack?: boolean;
goBack?: () => void;
Expand All @@ -98,7 +102,8 @@ const PlansStepAdaptor: StepType< {
};
};
} > = ( props ) => {
const { isInSignup, isStepperUpgradeFlow, selectedFeature, wrapperProps } = props;
const { displayedIntervals, isInSignup, isStepperUpgradeFlow, selectedFeature, wrapperProps } =
props;
const [ stepState, setStepState ] = useStepPersistedState< ProvidedDependencies >( 'plans-step' );
const siteSlug = useSiteSlug();

Expand Down Expand Up @@ -231,6 +236,7 @@ const PlansStepAdaptor: StepType< {
onIntentChange={ handleIntentChange }
onPlanIntervalUpdate={ onPlanIntervalUpdate }
intervalType={ planInterval }
displayedIntervals={ displayedIntervals }
wrapperProps={ {
hideBack: wrapperProps?.hideBack ?? false,
goBack: wrapperProps?.goBack ?? props.navigation.goBack,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,16 @@ function UnifiedPlansStep( {

if ( intent === 'plans-wordpress-hosting' ) {
return translate( 'Managed hosting without limits' );
} else if ( intent === 'plans-website-builder' ) {
}

if ( intent === 'plans-website-builder' ) {
return translate( 'Create a beautiful WordPress website' );
}

if ( intent === 'plans-woo-hosted' ) {
return translate( 'Select a plan to launch your store' );
}

return translate( 'There’s a plan for you' );
};

Expand Down Expand Up @@ -455,6 +462,10 @@ function UnifiedPlansStep( {
return null; // Use PlansFeaturesMain subheader for website-builder
}

if ( intent === 'plans-woo-hosted' ) {
return translate( 'Your free trial ends soon - select a plan to keep your online store.' );
}

if ( useEmailOnboardingSubheader ) {
return translate(
'Add more features to your professional website with a plan. Or {{link}}start with email and a free site{{/link}}.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { MinimalRequestCartProduct } from '@automattic/shopping-cart';
import { getPlanCartItem } from 'calypso/lib/cart-values/cart-items';
import { UnifiedPlansStepProps } from './unified-plans-step';

type SupportedIntervalTypes = Extract<
export type SupportedIntervalTypes = Extract<
UrlFriendlyTermType,
'monthly' | 'yearly' | '2yearly' | '3yearly'
>;
Expand Down
7 changes: 7 additions & 0 deletions client/landing/stepper/declarative-flow/registered-flows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
DOMAIN_AND_PLAN_FLOW,
PLAN_UPGRADE_FLOW,
FLEX_SITE_FLOW,
WOO_HOSTED_PLANS_FLOW,
} from '@automattic/onboarding';
import type { Flow, FlowV2 } from '../declarative-flow/internals/types';

Expand Down Expand Up @@ -51,11 +52,17 @@ const availableFlows: Record< string, () => Promise< { default: FlowV2< any > }
import(
/* webpackChunkName: "ai-site-builder-spec-flow" */ './flows/ai-site-builder-spec/ai-site-builder-spec'
),

[ PLAN_UPGRADE_FLOW ]: () =>
import( /* webpackChunkName: "plan-upgrade-flow" */ './flows/plan-upgrade/plan-upgrade' ),

[ FLEX_SITE_FLOW ]: () =>
import( /* webpackChunkName: "flex-site-flow" */ './flows/flex-site/flex-site' ),

[ WOO_HOSTED_PLANS_FLOW ]: () =>
import(
/* webpackChunkName: "woo-hosted-plans" */ './flows/woo-hosted-plans/woo-hosted-plans'
),
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ export default function CheckoutMainContent( {
</Step.LinkButton>
</span>
}
hideLogo
/>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,10 @@ function getLoggedInPlansAction( {
} & UseActionHookProps ): GridAction {
// Use plan type matching instead of exact slug matching for the 'plans-upgrade' intent.
// This allows monthly/yearly versions of the same plan to be considered "current"
const isUpgradeFlow =
plansIntent && [ 'plans-upgrade', 'plans-woo-hosted' ].includes( plansIntent );
const current =
plansIntent === 'plans-upgrade' && sitePlanSlug
isUpgradeFlow && sitePlanSlug
? getPlanClass( sitePlanSlug ) === getPlanClass( planSlug )
: sitePlanSlug === planSlug;
const isTrialPlan =
Expand Down Expand Up @@ -448,7 +450,7 @@ function getLoggedInPlansAction( {
// All actions for the current plan
if ( current ) {
// For the plans-upgrade intent, show "Your plan" as a non-clickable indicator
if ( plansIntent === 'plans-upgrade' ) {
if ( isUpgradeFlow ) {
return {
primary: {
callback: () => {},
Expand Down
2 changes: 2 additions & 0 deletions packages/api-core/src/upgrades/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export interface Purchase {
*/
is_domain_registration: boolean;

is_trial_plan: boolean;
is_pending_registration: boolean;
is_free_jetpack_stats_product: boolean;
is_jetpack_backup_t1: boolean;
Expand All @@ -206,6 +207,7 @@ export interface Purchase {
is_locked: boolean;
is_plan: boolean;
is_rechargable: boolean;
is_woo_hosted_product: boolean;

/**
* Determine if this is a kind of subscription that can currently be manually
Expand Down
Loading