From aefd48c00194fb016d8816e818e446aca6aa58bb Mon Sep 17 00:00:00 2001 From: James Kenneth Guidaven Date: Wed, 27 Mar 2024 14:12:57 +0800 Subject: [PATCH] A4A: Add Hosting section. (#88755) * Convert filter search component as reusable component. * Move listing section as a shared component. * Move useProductAndPlans as a shared hook. * Make listing section icon as optional. * Add Pressable filter support in useProductAndPlans hook. * Add Hosting section skeleton. * Remove unused CSS codes. * Implement Hosting card. * Fix explore button redirect. * Add WPCOM and Pressable section. * Address PR comments. --- .../components/filter-search/index.tsx | 17 +++++ .../components/filter-search/style.scss | 28 +++++++ .../product-listing => }/constants.ts | 1 + .../sections/marketplace/controller.tsx | 14 ++++ .../hooks/use-product-and-plans.tsx | 13 +++- .../hooks/use-hosting-description.ts | 39 ++++++++++ .../hosting-overview/hosting-card/index.tsx | 62 +++++++++++++++ .../hosting-overview/hosting-card/style.scss | 30 ++++++++ .../hosting-overview/hosting-list/index.tsx | 74 ++++++++++++++++++ .../hosting-overview/hosting-list/style.scss | 12 +++ .../marketplace/hosting-overview/index.tsx | 5 +- .../sections/marketplace/index.tsx | 11 +++ .../sections/marketplace/lib/hosting.tsx | 63 ++++++++++++++++ .../lib/is-pressable-hosting-product.ts | 8 -- .../marketplace/listing-section/index.tsx | 32 ++++++++ .../marketplace/listing-section/style.scss | 51 +++++++++++++ .../marketplace/pressable-overview/index.tsx | 66 ++++++++++++++++ .../product-listing/index.tsx | 27 +++---- .../product-listing/product-filter-search.tsx | 23 ------ .../product-listing/sections.tsx | 30 -------- .../product-listing/style.scss | 75 ------------------- .../sections/marketplace/types.ts | 2 + .../marketplace/wpcom-overview/index.tsx | 66 ++++++++++++++++ .../a8c-for-agencies/pressable-logo.svg | 59 +++++++++++++++ .../images/a8c-for-agencies/wpcom-logo.svg | 27 +++++++ client/sections.js | 2 + 26 files changed, 686 insertions(+), 151 deletions(-) create mode 100644 client/a8c-for-agencies/components/filter-search/index.tsx create mode 100644 client/a8c-for-agencies/components/filter-search/style.scss rename client/a8c-for-agencies/sections/marketplace/{products-overview/product-listing => }/constants.ts (81%) rename client/a8c-for-agencies/sections/marketplace/{products-overview/product-listing => }/hooks/use-product-and-plans.tsx (94%) create mode 100644 client/a8c-for-agencies/sections/marketplace/hosting-overview/hooks/use-hosting-description.ts create mode 100644 client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/index.tsx create mode 100644 client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/style.scss create mode 100644 client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/index.tsx create mode 100644 client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/style.scss create mode 100644 client/a8c-for-agencies/sections/marketplace/lib/hosting.tsx delete mode 100644 client/a8c-for-agencies/sections/marketplace/lib/is-pressable-hosting-product.ts create mode 100644 client/a8c-for-agencies/sections/marketplace/listing-section/index.tsx create mode 100644 client/a8c-for-agencies/sections/marketplace/listing-section/style.scss create mode 100644 client/a8c-for-agencies/sections/marketplace/pressable-overview/index.tsx delete mode 100644 client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/product-filter-search.tsx delete mode 100644 client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/sections.tsx create mode 100644 client/a8c-for-agencies/sections/marketplace/wpcom-overview/index.tsx create mode 100644 client/assets/images/a8c-for-agencies/pressable-logo.svg create mode 100644 client/assets/images/a8c-for-agencies/wpcom-logo.svg diff --git a/client/a8c-for-agencies/components/filter-search/index.tsx b/client/a8c-for-agencies/components/filter-search/index.tsx new file mode 100644 index 00000000000000..37a75e1566913e --- /dev/null +++ b/client/a8c-for-agencies/components/filter-search/index.tsx @@ -0,0 +1,17 @@ +import Search from 'calypso/components/search'; + +import './style.scss'; + +type Props = { + label: string; + onSearch: ( value: string ) => void; + onClick?: () => void; +}; + +export default function FilterSearch( { onSearch, onClick, label }: Props ) { + return ( +
+ +
+ ); +} diff --git a/client/a8c-for-agencies/components/filter-search/style.scss b/client/a8c-for-agencies/components/filter-search/style.scss new file mode 100644 index 00000000000000..457ceb2c092fdd --- /dev/null +++ b/client/a8c-for-agencies/components/filter-search/style.scss @@ -0,0 +1,28 @@ +@import "@wordpress/base-styles/breakpoints"; +@import "@wordpress/base-styles/mixins"; + +.a4a-filter-search { + flex-basis: 100%; + + @include break-xlarge { + flex-basis: 360px; + } + + .search { + &.is-open { + height: 46px; + + @include breakpoint-deprecated( ">660px" ) { + height: 33px; + } + } + margin-block-end: 0; + border: 1px solid var(--color-neutral-10); + } + + .search__input.form-text-input[type="search"] { + font-size: rem(13px); + font-weight: 400; + color: var(--color-text); + } +} diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/constants.ts b/client/a8c-for-agencies/sections/marketplace/constants.ts similarity index 81% rename from client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/constants.ts rename to client/a8c-for-agencies/sections/marketplace/constants.ts index 375eefdefae8a6..0569883ca644ee 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/constants.ts +++ b/client/a8c-for-agencies/sections/marketplace/constants.ts @@ -1,5 +1,6 @@ export const PRODUCT_FILTER_ALL = ''; export const PRODUCT_FILTER_PLANS = 'plans'; export const PRODUCT_FILTER_PRODUCTS = 'products'; +export const PRODUCT_FILTER_PRESSABLE_PLANS = 'pressable-plans'; export const PRODUCT_FILTER_WOOCOMMERCE_EXTENSIONS = 'woocommerce-extensions'; export const PRODUCT_FILTER_VAULTPRESS_BACKUP_ADDONS = 'vaultpress-backup-addons'; diff --git a/client/a8c-for-agencies/sections/marketplace/controller.tsx b/client/a8c-for-agencies/sections/marketplace/controller.tsx index 3a2d84baed0b0e..6a47fb887ca63f 100644 --- a/client/a8c-for-agencies/sections/marketplace/controller.tsx +++ b/client/a8c-for-agencies/sections/marketplace/controller.tsx @@ -6,8 +6,10 @@ import MarketplaceSidebar from '../../components/sidebar-menu/marketplace'; import AssignLicense from './assign-license'; import Checkout from './checkout'; import HostingOverview from './hosting-overview'; +import PressableOverview from './pressable-overview'; import DownloadProducts from './primary/download-products'; import ProductsOverview from './products-overview'; +import WpcomOverview from './wpcom-overview'; export const marketplaceContext: Callback = () => { page.redirect( A4A_MARKETPLACE_PRODUCTS_LINK ); @@ -26,6 +28,18 @@ export const marketplaceHostingContext: Callback = ( context, next ) => { next(); }; +export const marketplacePressableContext: Callback = ( context, next ) => { + context.secondary = ; + context.primary = ; + next(); +}; + +export const marketplaceWpcomContext: Callback = ( context, next ) => { + context.secondary = ; + context.primary = ; + next(); +}; + export const checkoutContext: Callback = ( context, next ) => { context.secondary = ; context.primary = ; diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/hooks/use-product-and-plans.tsx b/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx similarity index 94% rename from client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/hooks/use-product-and-plans.tsx rename to client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx index 856c7d78ba3107..557ab6731fb1c7 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/hooks/use-product-and-plans.tsx +++ b/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx @@ -9,14 +9,15 @@ import { isProductMatch } from 'calypso/jetpack-cloud/sections/partner-portal/pr import { useSelector } from 'calypso/state'; import { getAssignedPlanAndProductIDsForSite } from 'calypso/state/partner-portal/licenses/selectors'; import { APIProductFamilyProduct } from 'calypso/state/partner-portal/types'; -import isPressableHostingProduct from '../../../lib/is-pressable-hosting-product'; import { PRODUCT_FILTER_ALL, PRODUCT_FILTER_PLANS, + PRODUCT_FILTER_PRESSABLE_PLANS, PRODUCT_FILTER_PRODUCTS, PRODUCT_FILTER_VAULTPRESS_BACKUP_ADDONS, PRODUCT_FILTER_WOOCOMMERCE_EXTENSIONS, } from '../constants'; +import { isPressableHostingProduct } from '../lib/hosting'; import type { SiteDetails } from '@automattic/data-stores'; // Plans and Products that we can merged into 1 card. @@ -64,6 +65,12 @@ const getProductsAndPlansByFilter = ( allProductsAndPlans?.filter( ( { family_slug } ) => isWooCommerceProduct( family_slug ) ) || [] ); + case PRODUCT_FILTER_PRESSABLE_PLANS: + return ( + allProductsAndPlans?.filter( ( { family_slug } ) => + isPressableHostingProduct( family_slug ) + ) || [] + ); } return allProductsAndPlans || []; @@ -174,6 +181,10 @@ export default function useProductAndPlans( { PRODUCT_FILTER_WOOCOMMERCE_EXTENSIONS, filteredProductsAndBundles ), + pressablePlans: getProductsAndPlansByFilter( + PRODUCT_FILTER_PRESSABLE_PLANS, + filteredProductsAndBundles + ), suggestedProductSlugs, }; }, [ diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/hooks/use-hosting-description.ts b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hooks/use-hosting-description.ts new file mode 100644 index 00000000000000..e4d9b16459fa29 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hooks/use-hosting-description.ts @@ -0,0 +1,39 @@ +import { TranslateResult, useTranslate } from 'i18n-calypso'; +import { useMemo } from 'react'; + +/** + * Returns hosting name and description with given product slug. + * @param slug + * @returns + */ +export default function useHostingDescription( slug: string ): { + name: TranslateResult; + description: TranslateResult; +} { + const translate = useTranslate(); + + return useMemo( () => { + let description = ''; + let name = ''; + + switch ( slug ) { + case 'pressable-hosting': + name = translate( 'Pressable' ); + description = translate( + '9 custom plans built for agencies that include an intuitive control panel, easy site migration, staging environments, performance tools, and flexible upgrades & downgrades. ' + ); + break; + case 'wpcom-hosting': + name = translate( 'Wordpress.com' ); + description = translate( + 'Unbeatable uptime, unmetered bandwidth, and everything you need to streamline your development process, baked in. Perfect uptime. Fastest WP Bench score. A+ SSL grade.' + ); + break; + } + + return { + name, + description, + }; + }, [ slug, translate ] ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/index.tsx b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/index.tsx new file mode 100644 index 00000000000000..476e0b55d779f9 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/index.tsx @@ -0,0 +1,62 @@ +import { Button } from '@automattic/components'; +import { formatCurrency } from '@automattic/format-currency'; +import { useTranslate } from 'i18n-calypso'; +import { useDispatch } from 'calypso/state'; +import { recordTracksEvent } from 'calypso/state/analytics/actions'; +import { APIProductFamilyProduct } from 'calypso/state/partner-portal/types'; +import { getHostingLogo, getHostingPageUrl } from '../../lib/hosting'; +import useHostingDescription from '../hooks/use-hosting-description'; + +import './style.scss'; +type Props = { + plan: APIProductFamilyProduct; +}; + +export default function HostingCard( { plan }: Props ) { + const translate = useTranslate(); + const dispatch = useDispatch(); + + const { name, description } = useHostingDescription( plan.family_slug ); + + const onExploreClick = () => { + dispatch( + recordTracksEvent( 'calypso_marketplace_hosting_overview_explore_plan_click', { + hosting: plan.family_slug, + } ) + ); + }; + + return ( +
+
{ getHostingLogo( plan.family_slug ) }
+ +
+ + { translate( 'Starting at %(price)s', { + args: { price: formatCurrency( Number( plan.amount ), plan.currency ) }, + } ) } + +
+ { plan.price_interval === 'day' && translate( 'USD per plan per day' ) } + { plan.price_interval === 'month' && translate( 'USD per plan per month' ) } +
+
+ +

{ description }

+ + +
+ ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/style.scss b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/style.scss new file mode 100644 index 00000000000000..3475856027e7e9 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-card/style.scss @@ -0,0 +1,30 @@ + +.hosting-card { + padding: 1.5rem; + border: 1px solid var(--color-neutral-10); + border-radius: 4px; + user-select: none; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 24px; +} + +.hosting-card__price-value { + font-size: 1.5rem; + font-weight: 600; + line-height: 1.1; +} + +.hosting-card__price-interval { + font-size: 0.75rem; + line-height: 1.1; + font-weight: 400; +} + +.hosting-card__description { + font-size: 0.875rem; + line-height: 1.1; + font-weight: 400; + margin: 0; +} diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/index.tsx b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/index.tsx new file mode 100644 index 00000000000000..de86889b93aee7 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/index.tsx @@ -0,0 +1,74 @@ +import { SiteDetails } from '@automattic/data-stores'; +import { useTranslate } from 'i18n-calypso'; +import { useCallback, useMemo, useState } from 'react'; +import FilterSearch from 'calypso/a8c-for-agencies/components/filter-search'; +import { useDispatch } from 'calypso/state'; +import { recordTracksEvent } from 'calypso/state/analytics/actions'; +import useProductAndPlans from '../../hooks/use-product-and-plans'; +import { getCheapestPlan } from '../../lib/hosting'; +import ListingSection from '../../listing-section'; +import HostingCard from '../hosting-card'; + +import './style.scss'; + +interface Props { + selectedSite?: SiteDetails | null; +} + +export default function HostingList( { selectedSite }: Props ) { + const translate = useTranslate(); + const dispatch = useDispatch(); + + const [ productSearchQuery, setProductSearchQuery ] = useState< string >( '' ); + + const { isLoadingProducts, pressablePlans } = useProductAndPlans( { + selectedSite, + productSearchQuery, + } ); + + const cheapestPressablePlan = useMemo( + () => ( pressablePlans.length ? getCheapestPlan( pressablePlans ) : null ), + [ pressablePlans ] + ); + + const cheapestWPCOMPlan = cheapestPressablePlan + ? { ...cheapestPressablePlan, family_slug: 'wpcom-hosting' } + : null; // FIXME: Need to fetch from API + + const onProductSearch = useCallback( + ( value: string ) => { + setProductSearchQuery( value ); + dispatch( + recordTracksEvent( 'calypso_a4a_marketplace_hosting_overview_search_submit', { value } ) + ); + }, + [ dispatch ] + ); + + if ( isLoadingProducts ) { + return ( +
+
+
+ ); + } + + return ( +
+
+ +
+ + + { cheapestPressablePlan && } + { cheapestWPCOMPlan && } + +
+ ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/style.scss b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/style.scss new file mode 100644 index 00000000000000..1bbdf34a7ae2a9 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/hosting-list/style.scss @@ -0,0 +1,12 @@ +.hosting-list__placeholder { + @include placeholder( --color-neutral-10 ); + height: 43px; +} + +.hosting-list__actions { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin: 16px 0 32px; + justify-content: space-between; +} diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview/index.tsx b/client/a8c-for-agencies/sections/marketplace/hosting-overview/index.tsx index c3c6bfcc0be1c2..344a1603f4c55d 100644 --- a/client/a8c-for-agencies/sections/marketplace/hosting-overview/index.tsx +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview/index.tsx @@ -12,6 +12,7 @@ import MobileSidebarNavigation from 'calypso/a8c-for-agencies/components/sidebar import { A4A_MARKETPLACE_CHECKOUT_LINK } from 'calypso/a8c-for-agencies/components/sidebar-menu/lib/constants'; import useShoppingCart from '../hooks/use-shopping-cart'; import ShoppingCart from '../shopping-cart'; +import HostingList from './hosting-list'; export default function Hosting() { const translate = useTranslate(); @@ -42,7 +43,9 @@ export default function Hosting() { - Hosting here + + + ); } diff --git a/client/a8c-for-agencies/sections/marketplace/index.tsx b/client/a8c-for-agencies/sections/marketplace/index.tsx index 1258b736502282..ad03da7050df7e 100644 --- a/client/a8c-for-agencies/sections/marketplace/index.tsx +++ b/client/a8c-for-agencies/sections/marketplace/index.tsx @@ -3,6 +3,8 @@ import { A4A_MARKETPLACE_ASSIGN_LICENSE_LINK, A4A_MARKETPLACE_CHECKOUT_LINK, A4A_MARKETPLACE_HOSTING_LINK, + A4A_MARKETPLACE_HOSTING_PRESSABLE_LINK, + A4A_MARKETPLACE_HOSTING_WPCOM_LINK, A4A_MARKETPLACE_LINK, A4A_MARKETPLACE_PRODUCTS_LINK, A4A_MARKETPLACE_DOWNLOAD_PRODUCTS_LINK, @@ -13,8 +15,10 @@ import { checkoutContext, marketplaceContext, marketplaceHostingContext, + marketplacePressableContext, marketplaceProductsContext, downloadProductsContext, + marketplaceWpcomContext, } from './controller'; export default function () { @@ -22,6 +26,13 @@ export default function () { page( A4A_MARKETPLACE_LINK, marketplaceContext, makeLayout, clientRender ); page( A4A_MARKETPLACE_PRODUCTS_LINK, marketplaceProductsContext, makeLayout, clientRender ); page( A4A_MARKETPLACE_HOSTING_LINK, marketplaceHostingContext, makeLayout, clientRender ); + page( + A4A_MARKETPLACE_HOSTING_PRESSABLE_LINK, + marketplacePressableContext, + makeLayout, + clientRender + ); + page( A4A_MARKETPLACE_HOSTING_WPCOM_LINK, marketplaceWpcomContext, makeLayout, clientRender ); page( A4A_MARKETPLACE_CHECKOUT_LINK, checkoutContext, makeLayout, clientRender ); page( A4A_MARKETPLACE_ASSIGN_LICENSE_LINK, assignLicenseContext, makeLayout, clientRender ); page( A4A_MARKETPLACE_DOWNLOAD_PRODUCTS_LINK, downloadProductsContext, makeLayout, clientRender ); diff --git a/client/a8c-for-agencies/sections/marketplace/lib/hosting.tsx b/client/a8c-for-agencies/sections/marketplace/lib/hosting.tsx new file mode 100644 index 00000000000000..ec1a11ee03cfd2 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/lib/hosting.tsx @@ -0,0 +1,63 @@ +import { + A4A_MARKETPLACE_HOSTING_LINK, + A4A_MARKETPLACE_HOSTING_PRESSABLE_LINK, + A4A_MARKETPLACE_HOSTING_WPCOM_LINK, +} from 'calypso/a8c-for-agencies/components/sidebar-menu/lib/constants'; +import PressableLogo from 'calypso/assets/images/a8c-for-agencies/pressable-logo.svg'; +import WPCOMLogo from 'calypso/assets/images/a8c-for-agencies/wpcom-logo.svg'; +import { APIProductFamilyProduct } from 'calypso/state/partner-portal/types'; + +/** + * Get the cheapest plan from a list of plans + * @param {APIProductFamilyProduct[]} plans - List of plans + * @returns {APIProductFamilyProduct} - Cheapest plan + */ +export function getCheapestPlan( plans: APIProductFamilyProduct[] ) { + return plans.reduce( ( cheapestPlan: APIProductFamilyProduct, plan: APIProductFamilyProduct ) => { + if ( Number( plan.amount ) < Number( cheapestPlan.amount ) ) { + return plan; + } + return cheapestPlan; + }, plans[ 0 ] ); +} + +/** + * Get the URL for a hosting provider + * @param {string} slug - Hosting provider slug + * @returns {string} - Hosting provider URL + */ +export function getHostingPageUrl( slug: string ) { + switch ( slug ) { + case 'pressable-hosting': + return A4A_MARKETPLACE_HOSTING_PRESSABLE_LINK; + case 'wpcom-hosting': + return A4A_MARKETPLACE_HOSTING_WPCOM_LINK; + } + + return A4A_MARKETPLACE_HOSTING_LINK; +} + +/** + * Get the logo for a hosting provider + * @param {string} slug - Hosting provider slug + * @returns {Element} - Hosting provider logo + */ +export function getHostingLogo( slug: string ) { + switch ( slug ) { + case 'pressable-hosting': + return ; + case 'wpcom-hosting': + return ; + } + + return null; +} + +/** + * Provided a license key or a product slug, can we trust that the product is a Pressable hosting product + * @param keyOrSlug string + * @returns boolean True if Pressable hosting product, false if not + */ +export function isPressableHostingProduct( keyOrSlug: string ) { + return keyOrSlug.startsWith( 'pressable-hosting' ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/lib/is-pressable-hosting-product.ts b/client/a8c-for-agencies/sections/marketplace/lib/is-pressable-hosting-product.ts deleted file mode 100644 index 8825c1a5d8f044..00000000000000 --- a/client/a8c-for-agencies/sections/marketplace/lib/is-pressable-hosting-product.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Provided a license key or a product slug, can we trust that the product is a Pressable hosting product - * @param keyOrSlug string - * @returns boolean True if Pressable hosting product, false if not - */ -export default function isPressableHostingProduct( keyOrSlug: string ) { - return keyOrSlug.startsWith( 'pressable-hosting' ); -} diff --git a/client/a8c-for-agencies/sections/marketplace/listing-section/index.tsx b/client/a8c-for-agencies/sections/marketplace/listing-section/index.tsx new file mode 100644 index 00000000000000..23df3c288e385c --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/listing-section/index.tsx @@ -0,0 +1,32 @@ +import classNames from 'classnames'; +import { ReactNode } from 'react'; + +import './style.scss'; + +type Props = { + icon?: ReactNode; + title: string; + description: string; + children: ReactNode; + isTwoColumns?: boolean; +}; + +export default function ListingSection( { + icon, + title, + description, + children, + isTwoColumns, +}: Props ) { + return ( +
+

+ { icon } + { title } +

+

{ description }

+ +
{ children }
+
+ ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/listing-section/style.scss b/client/a8c-for-agencies/sections/marketplace/listing-section/style.scss new file mode 100644 index 00000000000000..aa9b4c96ebffe3 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/listing-section/style.scss @@ -0,0 +1,51 @@ +@import "@wordpress/base-styles/breakpoints"; +@import "@wordpress/base-styles/mixins"; + +.listing-section { + margin-block-end: 32px; +} + +h2.listing-section-title { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + margin-block-end: 10px; + font-size: rem(20px); + font-weight: 600; + line-height: 1.2; + + hr { + color: var(--color-gray-5); + width: 1px; + flex-grow: 1; + margin: 0; + } +} + +p.listing-section-description { + font-size: rem(14px); + font-weight: 400; + line-height: 1.2; + margin-block-end: 32px; +} + +.listing-section-content { + display: grid; + gap: 16px; + grid-template-columns: 1fr; + + @include break-large { + grid-template-columns: repeat(2, 1fr); + } + + @include break-wide { + grid-template-columns: repeat(3, 1fr); + } +} + +.listing-section.is-two-columns .listing-section-content { + @include break-wide { + grid-template-columns: repeat(2, 1fr); + } +} diff --git a/client/a8c-for-agencies/sections/marketplace/pressable-overview/index.tsx b/client/a8c-for-agencies/sections/marketplace/pressable-overview/index.tsx new file mode 100644 index 00000000000000..bc1521d45f2fa0 --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/pressable-overview/index.tsx @@ -0,0 +1,66 @@ +import page from '@automattic/calypso-router'; +import { useTranslate } from 'i18n-calypso'; +import Layout from 'calypso/a8c-for-agencies/components/layout'; +import LayoutBody from 'calypso/a8c-for-agencies/components/layout/body'; +import LayoutHeader, { + LayoutHeaderBreadcrumb as Breadcrumb, + LayoutHeaderActions as Actions, +} from 'calypso/a8c-for-agencies/components/layout/header'; +import LayoutTop from 'calypso/a8c-for-agencies/components/layout/top'; +import MobileSidebarNavigation from 'calypso/a8c-for-agencies/components/sidebar/mobile-sidebar-navigation'; +import { + A4A_MARKETPLACE_CHECKOUT_LINK, + A4A_MARKETPLACE_HOSTING_LINK, + A4A_MARKETPLACE_LINK, +} from 'calypso/a8c-for-agencies/components/sidebar-menu/lib/constants'; +import useShoppingCart from '../hooks/use-shopping-cart'; +import ShoppingCart from '../shopping-cart'; + +export default function PressableOverview() { + const translate = useTranslate(); + + const { selectedCartItems, onRemoveCartItem } = useShoppingCart(); + + return ( + } + > + + + + + + { + page( A4A_MARKETPLACE_CHECKOUT_LINK ); + } } + /> + + + + + Pressable hosting here + + ); +} diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/index.tsx b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/index.tsx index 501ec419a9d805..322fe7873dc77f 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/index.tsx +++ b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/index.tsx @@ -11,14 +11,14 @@ import { } from 'calypso/jetpack-cloud/sections/partner-portal/primary/issue-license/lib/incompatible-products'; import { useDispatch } from 'calypso/state'; import { recordTracksEvent } from 'calypso/state/analytics/actions'; +import FilterSearch from '../../../../components/filter-search'; import { ShoppingCartContext } from '../../context'; +import useProductAndPlans from '../../hooks/use-product-and-plans'; +import ListingSection from '../../listing-section'; import MultiProductCard from '../multi-product-card'; import ProductCard from '../product-card'; -import useProductAndPlans from './hooks/use-product-and-plans'; import { getSupportedBundleSizes, useProductBundleSize } from './hooks/use-product-bundle-size'; import useSubmitForm from './hooks/use-submit-form'; -import ProductFilterSearch from './product-filter-search'; -import ProductListingSection from './sections'; import VolumePriceSelector from './volume-price-selector'; import type { ShoppingCartItem } from '../../types'; import type { SiteDetails } from '@automattic/data-stores'; @@ -278,8 +278,9 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod
- @@ -293,7 +294,7 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod
{ plans.length > 0 && ( - } title={ translate( 'Jetpack Plans' ) } description={ translate( @@ -302,11 +303,11 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod isTwoColumns > { getProductCards( plans ) } - + ) } { products.length > 0 && ( - } title={ translate( 'Jetpack Products' ) } description={ translate( @@ -314,11 +315,11 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod ) } > { getProductCards( products ) } - + ) } { wooExtensions.length > 0 && ( - } title={ translate( 'WooCommerce Extensions' ) } description={ translate( @@ -326,11 +327,11 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod ) } > { getProductCards( wooExtensions ) } - + ) } { backupAddons.length > 0 && ( - } title={ translate( 'Jetpack VaultPress Backup Add-ons' ) } description={ translate( @@ -338,7 +339,7 @@ export default function ProductListing( { selectedSite, suggestedProduct }: Prod ) } > { getProductCards( backupAddons ) } - + ) }
); diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/product-filter-search.tsx b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/product-filter-search.tsx deleted file mode 100644 index 1c03dc865ca4f9..00000000000000 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/product-filter-search.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useTranslate } from 'i18n-calypso'; -import Search from 'calypso/components/search'; - -type Props = { - onProductSearch: ( value: string ) => void; - onClick?: () => void; -}; - -export default function ProductFilterSearch( { onProductSearch, onClick }: Props ) { - const translate = useTranslate(); - - return ( -
- -
- ); -} diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/sections.tsx b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/sections.tsx deleted file mode 100644 index de27194951b1bf..00000000000000 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/sections.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import classNames from 'classnames'; -import { ReactNode } from 'react'; - -type Props = { - icon: ReactNode; - title: string; - description: string; - children: ReactNode; - isTwoColumns?: boolean; -}; - -export default function ProductListingSection( { - icon, - title, - description, - children, - isTwoColumns, -}: Props ) { - return ( -
-

- { icon } - { title } -

-

{ description }

- -
{ children }
-
- ); -} diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/style.scss b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/style.scss index 3f19335a7d7c2a..5cfebd907125e7 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/style.scss +++ b/client/a8c-for-agencies/sections/marketplace/products-overview/product-listing/style.scss @@ -7,55 +7,6 @@ height: 43px; } -.product-listing__section { - margin-block-end: 32px; -} - -h2.product-listing__section-title { - display: flex; - flex-direction: row; - align-items: center; - gap: 10px; - margin-block-end: 10px; - font-size: rem(20px); - font-weight: 600; - line-height: 1.2; - - hr { - color: var(--color-gray-5); - width: 1px; - flex-grow: 1; - margin: 0; - } -} - -p.product-listing__section-description { - font-size: rem(14px); - font-weight: 400; - line-height: 1.2; - margin-block-end: 32px; -} - -.product-listing__section-content { - display: grid; - gap: 16px; - grid-template-columns: 1fr; - - @include break-large { - grid-template-columns: repeat(2, 1fr); - } - - @include break-wide { - grid-template-columns: repeat(3, 1fr); - } -} - -.product-listing__section.is-two-columns .product-listing__section-content { - @include break-wide { - grid-template-columns: repeat(2, 1fr); - } -} - p.product-listing__description { flex: 1 1 auto; align-self: flex-end; @@ -132,32 +83,6 @@ p.product-listing__description { } } -.product-listing__filter-search { - flex-basis: 100%; - - @include break-xlarge { - flex-basis: 360px; - } - - .search { - &.is-open { - height: 46px; - - @include breakpoint-deprecated( ">660px" ) { - height: 33px; - } - } - margin-block-end: 0; - border: 1px solid var(--color-neutral-10); - } - - .search__input.form-text-input[type="search"] { - font-size: rem(13px); - font-weight: 400; - color: var(--color-text); - } -} - .product-listing__volume-price-selector { min-width: 100%; diff --git a/client/a8c-for-agencies/sections/marketplace/types.ts b/client/a8c-for-agencies/sections/marketplace/types.ts index 27e51e25964e7a..792cae26251acb 100644 --- a/client/a8c-for-agencies/sections/marketplace/types.ts +++ b/client/a8c-for-agencies/sections/marketplace/types.ts @@ -15,3 +15,5 @@ export interface AssignLicenseProps { suggestedProduct?: string; quantity?: number; } + +export type HostingType = 'pressable-hosting' | 'wpcom-hosting'; diff --git a/client/a8c-for-agencies/sections/marketplace/wpcom-overview/index.tsx b/client/a8c-for-agencies/sections/marketplace/wpcom-overview/index.tsx new file mode 100644 index 00000000000000..8623d0decb415e --- /dev/null +++ b/client/a8c-for-agencies/sections/marketplace/wpcom-overview/index.tsx @@ -0,0 +1,66 @@ +import page from '@automattic/calypso-router'; +import { useTranslate } from 'i18n-calypso'; +import Layout from 'calypso/a8c-for-agencies/components/layout'; +import LayoutBody from 'calypso/a8c-for-agencies/components/layout/body'; +import LayoutHeader, { + LayoutHeaderBreadcrumb as Breadcrumb, + LayoutHeaderActions as Actions, +} from 'calypso/a8c-for-agencies/components/layout/header'; +import LayoutTop from 'calypso/a8c-for-agencies/components/layout/top'; +import MobileSidebarNavigation from 'calypso/a8c-for-agencies/components/sidebar/mobile-sidebar-navigation'; +import { + A4A_MARKETPLACE_CHECKOUT_LINK, + A4A_MARKETPLACE_HOSTING_LINK, + A4A_MARKETPLACE_LINK, +} from 'calypso/a8c-for-agencies/components/sidebar-menu/lib/constants'; +import useShoppingCart from '../hooks/use-shopping-cart'; +import ShoppingCart from '../shopping-cart'; + +export default function WpcomOverview() { + const translate = useTranslate(); + + const { selectedCartItems, onRemoveCartItem } = useShoppingCart(); + + return ( + } + > + + + + + + { + page( A4A_MARKETPLACE_CHECKOUT_LINK ); + } } + /> + + + + + Wordpress.com hosting here + + ); +} diff --git a/client/assets/images/a8c-for-agencies/pressable-logo.svg b/client/assets/images/a8c-for-agencies/pressable-logo.svg new file mode 100644 index 00000000000000..4bf501fe0a8e95 --- /dev/null +++ b/client/assets/images/a8c-for-agencies/pressable-logo.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/images/a8c-for-agencies/wpcom-logo.svg b/client/assets/images/a8c-for-agencies/wpcom-logo.svg new file mode 100644 index 00000000000000..1eccc58ecf8279 --- /dev/null +++ b/client/assets/images/a8c-for-agencies/wpcom-logo.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/client/sections.js b/client/sections.js index bdef4188cdd8aa..182d96ddfda8df 100644 --- a/client/sections.js +++ b/client/sections.js @@ -733,6 +733,8 @@ const sections = [ '/marketplace', '/marketplace/products', '/marketplace/hosting', + '/marketplace/hosting/pressable', + '/marketplace/hosting/wpcom', '/marketplace/checkout', '/marketplace/assign-license', '/marketplace/download-products',