-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Task: Create Plans page for Jetpack App Site Creation (#82135)
* Create a basic structure for /jetpack-app-plans page - Created jetpack-app-plans directory - Added an entry into sections.js with a path and module - Created controller.jsx that displays JetpackAppPlans component and passes query parameters - Created index.js defining a page and including jetpackAppPlans controller - Created main.jsx that shows a loading indicator while querying plans, and PlansFeaturesMain component once plans are loaded - Created style.scss with jetpack-app-plans style that includes plan-features-layout-switcher to support media queries when displaying plans grid * Remove use of deprecated params * Set intent for Jetpack app plans in site creation * Hide yearly/monthly selector * Pass selected plan through redirection url - Allow redirect_to parameter to URL which is then used to callback with a selected plan - Do not pass selected plan if free plan is selected * Use snake case for passing cart item * Fix plan retrieval from cart Use getPlanCartItem to get plan * Check if cartItems exist before fetching plan * Hide top and sidebars in jetpack-app-plans flow * Add a header to jetpack-app-plans page * Refactor to improve code style * Show a different header label when no domain name is passed * Hide feature comparison * Rename domain_name to paid_domain_name for more clarity * Reuse the same translation from previous label * Renaming plans-grid path * Simplify redirect by passing only plan product id * Remove redundant flow name * Set a lighter web view color for Jetpack App Plans to match other plans page shown on the app * Create a middleware for Jetpack App pages layout Jetpack App pages layout do not require all the outer layouts of Calypso. We can omit them by creating a separate middleware that only displays the required component. * Change jetpack-app-plans to jetpack-app/plans route * Remove redundant style * Remove Loading and Plans subcomponents to improve readability * Use useTranslate hook * Convert JS to TS * Use --studio-white for Jetpack App Plans background color * Remove explicit { yes } from parameters * Use import type for imports only for typing purposes * Use a consistent Context definition * Use addQueryArgs for building redirection URL * Use a jetpack-app__ prefix for class names * Use grid-unit variables for spacing * Redirect to originalUrl with select plan_id and plan_slug * Handle an entire jetpack-app/ route Redirect to jetpack-app/plans when accessing jetpack-app route, since it's the only page of the route for now * Redirect to home if jetpack-app/ route is not accessed via the app * Added README.md explaining jetpack-app/ route * Remove forward slash from the section path --------- Co-authored-by: Paul Von Schrottky <[email protected]>
- Loading branch information
Showing
8 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Jetpack App | ||
|
||
This module is specifically designed for use within the Jetpack App to handle App-specific flows. These flows are intended to be executed only within the web views of the Jetpack App. Any access outside of this context will result in a redirection to the homepage. | ||
|
||
## Routes | ||
|
||
### Plans | ||
|
||
- **URL**: `jetpack-app/plans` | ||
- **Description**: This route opens a plans page that is specifically configured for the Jetpack App. It reuses the Plans component used in other pages. | ||
- **Configuration**: | ||
- The page can be configured through URL parameter `paid_domain_name`. | ||
- Plan selection can be received by observing page redirections with `plan_id` and `plan_slug` parameters. | ||
|
||
Example: | ||
``` | ||
# Access the plans page with a specific domain name which was selected prior on the app. | ||
https://www.wordpress.com/jetpack-app/plans?paid_domain_name=example.com | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import page from 'page'; | ||
import { isWpMobileApp } from 'calypso/lib/mobile-app'; | ||
import JetpackAppPlans from './plans/main'; | ||
|
||
export function jetpackAppPlans( context: PageJS.Context, next: () => void ) { | ||
context.primary = ( | ||
<JetpackAppPlans | ||
paidDomainName={ context.query.paid_domain_name } | ||
originalUrl={ context.originalUrl } | ||
/> | ||
); | ||
|
||
next(); | ||
} | ||
|
||
export function redirectIfNotJetpackApp( _context: PageJS.Context, next: () => void ) { | ||
if ( ! isWpMobileApp() ) { | ||
page.redirect( '/' ); | ||
} else { | ||
next(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import page from 'page'; | ||
import { render as clientRender } from 'calypso/controller'; | ||
import { jetpackAppPlans, redirectIfNotJetpackApp } from './controller'; | ||
import { makeJetpackAppLayout } from './page-middleware/layout'; | ||
|
||
export default function () { | ||
page( | ||
'/jetpack-app/plans', | ||
redirectIfNotJetpackApp, | ||
jetpackAppPlans, | ||
makeJetpackAppLayout, | ||
clientRender | ||
); | ||
|
||
// Default to /plans page until we have more pages | ||
page( '/jetpack-app', '/jetpack-app/plans' ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import { Provider as ReduxProvider } from 'react-redux'; | ||
import CalypsoI18nProvider from 'calypso/components/calypso-i18n-provider'; | ||
import { RouteProvider } from 'calypso/components/route'; | ||
import { CalypsoReactQueryDevtools } from 'calypso/lib/react-query-devtools-helper'; | ||
import type { FunctionComponent } from 'react'; | ||
import type { Store } from 'redux'; | ||
|
||
export { render, hydrate } from 'calypso/controller/web-util'; | ||
|
||
interface ProviderWrappedLayoutProps { | ||
store: Store; | ||
queryClient: QueryClient; | ||
currentRoute: string; | ||
currentQuery: object; | ||
primary: React.ReactNode; | ||
redirectUri: string; | ||
} | ||
|
||
export const ProviderWrappedLayout: FunctionComponent< ProviderWrappedLayoutProps > = ( { | ||
store, | ||
queryClient, | ||
currentRoute, | ||
currentQuery, | ||
primary, | ||
} ) => { | ||
return ( | ||
<CalypsoI18nProvider> | ||
{ /* TS incorrectly infers RouteProvider types; ignore errors here. */ } | ||
{ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } | ||
{ /* @ts-ignore */ } | ||
<RouteProvider currentRoute={ currentRoute } currentQuery={ currentQuery }> | ||
<QueryClientProvider client={ queryClient }> | ||
<ReduxProvider store={ store }>{ primary }</ReduxProvider> | ||
<CalypsoReactQueryDevtools /> | ||
</QueryClientProvider> | ||
</RouteProvider> | ||
</CalypsoI18nProvider> | ||
); | ||
}; | ||
|
||
export function makeJetpackAppLayoutMiddleware( LayoutComponent: typeof ProviderWrappedLayout ) { | ||
return ( context: PageJS.Context, next: () => void ) => { | ||
const { store, queryClient, pathname, query, primary } = context; | ||
|
||
context.layout = ( | ||
<LayoutComponent | ||
store={ store } | ||
queryClient={ queryClient } | ||
currentRoute={ pathname } | ||
currentQuery={ query } | ||
primary={ primary } | ||
redirectUri={ context.originalUrl } | ||
/> | ||
); | ||
next(); | ||
}; | ||
} | ||
|
||
export const makeJetpackAppLayout = makeJetpackAppLayoutMiddleware( ProviderWrappedLayout ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { getPlan, PLAN_FREE } from '@automattic/calypso-products'; | ||
import { addQueryArgs } from '@wordpress/url'; | ||
import { useTranslate } from 'i18n-calypso'; | ||
import { useSelector } from 'react-redux'; | ||
import QueryPlans from 'calypso/components/data/query-plans'; | ||
import FormattedHeader from 'calypso/components/formatted-header'; | ||
import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; | ||
import Main from 'calypso/components/main'; | ||
import { getPlanCartItem } from 'calypso/lib/cart-values/cart-items'; | ||
import PlansFeaturesMain from 'calypso/my-sites/plans-features-main'; | ||
import { getPlanSlug } from 'calypso/state/plans/selectors'; | ||
import type { Plan } from '@automattic/calypso-products'; | ||
import type { MinimalRequestCartProduct } from '@automattic/shopping-cart'; | ||
import type { AppState } from 'calypso/types'; | ||
|
||
import './style.scss'; | ||
|
||
interface HeaderProps { | ||
paidDomainName?: string; | ||
} | ||
|
||
interface JetpackAppPlansProps { | ||
paidDomainName?: string; | ||
originalUrl: string; | ||
} | ||
|
||
const Header: React.FC< HeaderProps > = ( { paidDomainName } ) => { | ||
const translate = useTranslate(); | ||
|
||
return ( | ||
<div className="jetpack-app__plans-header"> | ||
<FormattedHeader | ||
brandFont | ||
headerText={ translate( 'Choose the perfect plan' ) } | ||
align="center" | ||
/> | ||
{ paidDomainName ? ( | ||
<> | ||
<p> | ||
{ translate( | ||
'With your annual plan, you’ll get %(domainName)s {{strong}}free for the first year{{/strong}}.', | ||
{ | ||
args: { | ||
domainName: paidDomainName, | ||
}, | ||
components: { strong: <strong /> }, | ||
} | ||
) } | ||
</p> | ||
<p> | ||
{ translate( | ||
'You’ll also unlock advanced features that make it easy to build and grow your site.' | ||
) } | ||
</p> | ||
</> | ||
) : ( | ||
<p>{ translate( 'See and compare the features available on each WordPress.com plan.' ) }</p> | ||
) } | ||
</div> | ||
); | ||
}; | ||
|
||
const JetpackAppPlans: React.FC< JetpackAppPlansProps > = ( { paidDomainName, originalUrl } ) => { | ||
const planSlug = useSelector( ( state: AppState ) => | ||
getPlanSlug( state, getPlan( PLAN_FREE )?.getProductId() || 0 ) | ||
) as string | null; | ||
const plansLoaded = Boolean( planSlug ); | ||
|
||
const onUpgradeClick = ( cartItems?: MinimalRequestCartProduct[] | null | undefined ) => { | ||
const productSlug = getPlanCartItem( cartItems )?.product_slug; | ||
|
||
type PlansParameters = { plan_id?: number; plan_slug: string }; | ||
let args: PlansParameters; | ||
|
||
if ( ! productSlug ) { | ||
args = { plan_slug: PLAN_FREE }; | ||
} else { | ||
const plan = getPlan( productSlug ) as Plan; | ||
args = { plan_id: plan.getProductId(), plan_slug: productSlug }; | ||
} | ||
|
||
window.location.href = addQueryArgs( originalUrl, args ); | ||
}; | ||
|
||
return ( | ||
<Main className="jetpack-app__plans"> | ||
<QueryPlans /> | ||
{ plansLoaded ? ( | ||
<> | ||
<Header paidDomainName={ paidDomainName } /> | ||
<PlansFeaturesMain | ||
paidDomainName={ paidDomainName } | ||
intent="plans-jetpack-app-site-creation" | ||
isInSignup | ||
intervalType="yearly" | ||
onUpgradeClick={ onUpgradeClick } | ||
plansWithScroll={ false } | ||
hidePlanTypeSelector | ||
hidePlansFeatureComparison | ||
/> | ||
</> | ||
) : ( | ||
<div className="jetpack-app__plans-loading"> | ||
<LoadingEllipsis /> | ||
</div> | ||
) } | ||
</Main> | ||
); | ||
}; | ||
|
||
export default JetpackAppPlans; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
@import "calypso/my-sites/plans-grid/media-queries"; | ||
@import "@wordpress/base-styles/variables"; | ||
@import "@automattic/typography/styles/fonts"; | ||
|
||
body { | ||
background-color: var(--studio-white); | ||
} | ||
|
||
.jetpack-app__plans { | ||
@include plan-features-layout-switcher; | ||
|
||
.jetpack-app__plans-header { | ||
display: block; | ||
overflow: auto; | ||
|
||
.formatted-header__title, | ||
.formatted-header__subtitle { | ||
max-width: unset; | ||
} | ||
|
||
h1 { | ||
font-family: $brand-serif; | ||
font-size: $font-headline-small; | ||
margin-bottom: $grid-unit-20; | ||
} | ||
|
||
p { | ||
font-size: $font-body; | ||
color: var(--color-text-subtle); | ||
text-align: center; | ||
padding: 0 $grid-unit-20; | ||
margin-bottom: 0; | ||
} | ||
} | ||
|
||
.jetpack-app__plans-loading { | ||
width: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
position: relative; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters