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

Task: Create Plans page for Jetpack App Site Creation #82135

Merged
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d0f33a4
Create a basic structure for /jetpack-app-plans page
staskus Sep 25, 2023
b02dc10
Remove use of deprecated params
guarani Sep 25, 2023
c5cfdc1
Set intent for Jetpack app plans in site creation
staskus Sep 26, 2023
377e11f
Hide yearly/monthly selector
staskus Sep 26, 2023
3c896d6
Pass selected plan through redirection url
staskus Sep 26, 2023
45e88f7
Use snake case for passing cart item
staskus Sep 26, 2023
15d3f53
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Sep 26, 2023
07e5c4a
Fix plan retrieval from cart
guarani Sep 26, 2023
0c741da
Check if cartItems exist before fetching plan
staskus Sep 27, 2023
20b2a8e
Hide top and sidebars in jetpack-app-plans flow
staskus Sep 27, 2023
46c9d1f
Add a header to jetpack-app-plans page
staskus Sep 27, 2023
a2cc114
Refactor to improve code style
staskus Sep 27, 2023
362bac9
Show a different header label when no domain name is passed
staskus Sep 27, 2023
60a48a9
Hide feature comparison
staskus Sep 27, 2023
ce04567
Rename domain_name to paid_domain_name for more clarity
staskus Sep 27, 2023
d4109ec
Reuse the same translation from previous label
staskus Sep 27, 2023
86616c6
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Sep 28, 2023
864bfe0
Renaming plans-grid path
staskus Sep 28, 2023
4d76322
Simplify redirect by passing only plan product id
staskus Oct 2, 2023
ea33f08
Remove redundant flow name
staskus Oct 4, 2023
4a2cd2d
Set a lighter web view color for Jetpack App Plans to match other pla…
staskus Oct 5, 2023
1e773e8
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Oct 5, 2023
6f08ea2
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Oct 6, 2023
a06919a
Create a middleware for Jetpack App pages layout
staskus Oct 9, 2023
b6eac81
Change jetpack-app-plans to jetpack-app/plans route
staskus Oct 9, 2023
1bcaf9f
Remove redundant style
staskus Oct 9, 2023
c7fe9e7
Remove Loading and Plans subcomponents to improve readability
staskus Oct 10, 2023
21a4d96
Use useTranslate hook
staskus Oct 10, 2023
9822307
Convert JS to TS
staskus Oct 10, 2023
b5f5ecf
Use --studio-white for Jetpack App Plans background color
staskus Oct 11, 2023
2f30134
Remove explicit { yes } from parameters
staskus Oct 11, 2023
af02274
Use import type for imports only for typing purposes
staskus Oct 11, 2023
910dd09
Use a consistent Context definition
staskus Oct 11, 2023
7d480b3
Use addQueryArgs for building redirection URL
staskus Oct 11, 2023
1cf554b
Use a jetpack-app__ prefix for class names
staskus Oct 11, 2023
e1b4a6b
Use grid-unit variables for spacing
staskus Oct 11, 2023
ada828f
Redirect to originalUrl with select plan_id and plan_slug
staskus Oct 11, 2023
a69a6a2
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Oct 11, 2023
0889d5c
Handle an entire jetpack-app/ route
staskus Oct 12, 2023
5a7dbaa
Redirect to home if jetpack-app/ route is not accessed via the app
staskus Oct 12, 2023
fe58732
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Oct 12, 2023
2554437
Added README.md explaining jetpack-app/ route
staskus Oct 12, 2023
d355851
Remove forward slash from the section path
staskus Oct 12, 2023
d18c027
Merge branch 'trunk' into task/82131-create-plans-page-for-jetpack-ap…
staskus Oct 12, 2023
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
22 changes: 22 additions & 0 deletions client/jetpack-app/controller.tsx
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();
}
}
17 changes: 17 additions & 0 deletions client/jetpack-app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import page from 'page';
staskus marked this conversation as resolved.
Show resolved Hide resolved
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',
staskus marked this conversation as resolved.
Show resolved Hide resolved
redirectIfNotJetpackApp,
jetpackAppPlans,
makeJetpackAppLayout,
clientRender
);

// Default to /plans page until we have more pages
page( '/jetpack-app', '/jetpack-app/plans' );
Copy link
Member

Choose a reason for hiding this comment

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

Note that when this lands in production, to access the new /jetpack-app route it in staging/production you'll need to request the route to be pointed to Calypso.

Here's an example recent request for another route: pMz3w-ilk-p2

}
60 changes: 60 additions & 0 deletions client/jetpack-app/page-middleware/layout.tsx
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 ) {
staskus marked this conversation as resolved.
Show resolved Hide resolved
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 );
111 changes: 111 additions & 0 deletions client/jetpack-app/plans/main.tsx
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 ? (
staskus marked this conversation as resolved.
Show resolved Hide resolved
<>
<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.'
staskus marked this conversation as resolved.
Show resolved Hide resolved
) }
</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;
staskus marked this conversation as resolved.
Show resolved Hide resolved
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
staskus marked this conversation as resolved.
Show resolved Hide resolved
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;
43 changes: 43 additions & 0 deletions client/jetpack-app/plans/style.scss
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;
staskus marked this conversation as resolved.
Show resolved Hide resolved
}

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type PlansIntent =
| 'plans-new-hosted-site'
| 'plans-plugins'
| 'plans-jetpack-app'
| 'plans-jetpack-app-site-creation'
| 'plans-import'
| 'plans-woocommerce'
| 'plans-paid-media'
Expand Down Expand Up @@ -217,6 +218,9 @@ const usePlanTypesWithIntent = ( {
case 'plans-jetpack-app':
planTypes = [ TYPE_PERSONAL, TYPE_PREMIUM, TYPE_BUSINESS, TYPE_ECOMMERCE ];
break;
case 'plans-jetpack-app-site-creation':
planTypes = [ TYPE_FREE, TYPE_PERSONAL, TYPE_PREMIUM, TYPE_BUSINESS, TYPE_ECOMMERCE ];
break;
case 'plans-paid-media':
planTypes = [ TYPE_PERSONAL, TYPE_PREMIUM, TYPE_BUSINESS, TYPE_ECOMMERCE ];
break;
Expand Down
5 changes: 5 additions & 0 deletions client/sections.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ const sections = [
enableLoggedOut: true,
isomorphic: true,
},
{
name: 'jetpack-app',
paths: [ '/jetpack-app' ],
module: 'calypso/jetpack-app/',
staskus marked this conversation as resolved.
Show resolved Hide resolved
},
{
name: 'stats',
paths: [ '/stats' ],
Expand Down