Skip to content

Commit

Permalink
Enable Simple Sites on Jetpack Cloud (#89508)
Browse files Browse the repository at this point in the history
* Test: Enable Simple Sites on Jetpack Cloud (Early classic only)

* Enable to all Simple sites

* Enable on staging & horizon

* Update Upsell to show WPcom plans

* Update Upsells for WPCOM plans

* Fix CSS

* Fixed space in directory

* Fix component import

Co-authored-by: Candy Tsai <candy02058912@gmail.com>

* Fix component import on Search

* Remove placeholder component

* Add plan features to WordAds Upsell

* Add upsell to Activity Log

* Activity Log: remove hardcoding starter plan for upsell

* Wrap upgrade in translate

* Remove hard code for Starter plan name

* Only enable simple sites for dev environment

* try enabling realtime-site-count in horizon

* Remove changes for Activity Log top upsell section

* only render Activity log Past upsell for simple sites

* add jetpack/manage-simple-sites to make it clear that this is for simple sites

* Add jetpack/manage-simple-sites to all environments

* Add wpcom back to site_filter for dev environment

* Add class upsell-product-wpcom-plan-card

---------

Co-authored-by: LuPuS <lupus@lps-fedora.lan>
Co-authored-by: Candy Tsai <candy02058912@gmail.com>
  • Loading branch information
3 people committed May 23, 2024
1 parent 96aa5e1 commit 702ad51
Show file tree
Hide file tree
Showing 17 changed files with 515 additions and 30 deletions.
26 changes: 26 additions & 0 deletions client/components/activity-card-list/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import {
PLAN_PERSONAL,
WPCOM_FEATURES_FULL_ACTIVITY_LOG,
getPlan,
} from '@automattic/calypso-products';
import { withMobileBreakpoint } from '@automattic/viewport-react';
import classNames from 'classnames';
import { localize } from 'i18n-calypso';
import PropTypes from 'prop-types';
import { Component, createRef } from 'react';
import { connect } from 'react-redux';
import ActivityCard from 'calypso/components/activity-card';
import PlanUpsellCard from 'calypso/components/activity-card/plan-upsell';
import QueryJetpackCredentialsStatus from 'calypso/components/data/query-jetpack-credentials-status';
import QueryRewindCapabilities from 'calypso/components/data/query-rewind-capabilities';
import QueryRewindPolicies from 'calypso/components/data/query-rewind-policies';
Expand All @@ -22,6 +28,8 @@ import getRewindPoliciesRequestStatus from 'calypso/state/rewind/selectors/get-r
import getActivityLogFilter from 'calypso/state/selectors/get-activity-log-filter';
import isRequestingSiteFeatures from 'calypso/state/selectors/is-requesting-site-features';
import isSiteAutomatedTransfer from 'calypso/state/selectors/is-site-automated-transfer';
import siteHasFeature from 'calypso/state/selectors/site-has-feature';
import { isSimpleSite } from 'calypso/state/sites/selectors';
import { getSelectedSiteId, getSelectedSiteSlug } from 'calypso/state/ui/selectors';
import VisibleDaysLimitUpsell from './visible-days-limit-upsell';

Expand Down Expand Up @@ -141,6 +149,16 @@ class ActivityCardList extends Component {
return logsByDate;
}

renderPlanUpsell( pageLogs ) {
if ( pageLogs.length === 0 ) {
return;
}

const upsellPlanName = getPlan( PLAN_PERSONAL )?.getTitle();

return <PlanUpsellCard upsellPlanName={ upsellPlanName } />;
}

renderLogs( pageLogs ) {
const {
applySiteOffset,
Expand Down Expand Up @@ -244,6 +262,8 @@ class ActivityCardList extends Component {
logs,
pageSize,
showPagination,
siteHasFullActivityLog,
isSimple,
} = this.props;

const visibleLimitCutoffDate = Number.isFinite( visibleDays )
Expand Down Expand Up @@ -280,6 +300,7 @@ class ActivityCardList extends Component {
total={ visibleLogs.length }
/>
) }
{ ! siteHasFullActivityLog && isSimple && this.renderPlanUpsell( pageLogs ) }
{ this.renderLogs( pageLogs ) }
{ showLimitUpsell && (
<VisibleDaysLimitUpsell cardClassName="activity-card-list__primary-card-with-more" />
Expand Down Expand Up @@ -388,7 +409,10 @@ const mapStateToProps = ( state ) => {
const rewindPoliciesRequestStatus = getRewindPoliciesRequestStatus( state, siteId );

const isAtomic = isSiteAutomatedTransfer( state, siteId );
const isSimple = isSimpleSite( state, siteId );
const requestingSiteFeatures = isRequestingSiteFeatures( state, siteId );
const siteHasFullActivityLog =
siteId && siteHasFeature( state, siteId, WPCOM_FEATURES_FULL_ACTIVITY_LOG );

return {
filter,
Expand All @@ -399,7 +423,9 @@ const mapStateToProps = ( state ) => {
siteSlug,
userLocale,
isAtomic,
isSimple,
isRequestingSiteFeatures: requestingSiteFeatures,
siteHasFullActivityLog,
};
};

Expand Down
20 changes: 20 additions & 0 deletions client/components/activity-card-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@
}
}
}
.activity-card-list__date-group-upsell {
.activity-card__actor,
.activity-card__activity-description,
.activity-card__activity-title {
filter: blur(10px);
}
.activity-card__activity-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
padding: 12px 0;
text-align: center;
width: 100%;

p.activity-card__activity-overlay-text {
font-size: $font-body;
}
}
}

@include breakpoint-deprecated( "<660px" ) {
.activity-card-list .filterbar__wrap.card {
Expand Down
80 changes: 80 additions & 0 deletions client/components/activity-card/plan-upsell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Card, Gridicon } from '@automattic/components';
import { useTranslate } from 'i18n-calypso';
import * as React from 'react';
import ActivityActor from 'calypso/components/activity-card/activity-actor';
import { applySiteOffset } from 'calypso/lib/site/timezone';
import { useSelector } from 'calypso/state';
import getSiteGmtOffset from 'calypso/state/selectors/get-site-gmt-offset';
import getSiteTimezoneValue from 'calypso/state/selectors/get-site-timezone-value';
import { getSelectedSiteId } from 'calypso/state/ui/selectors';
import type { Activity } from './types';

import './style.scss';

type OwnProps = {
className?: string;
summarize?: boolean;
shareable?: boolean;
activity: Activity;
availableActions?: Array< string >;
onClickClone?: ( period: string ) => void;
};

type Props = OwnProps & {
upsellPlanName: string;
};

const PlanUpsellCard: React.FC< Props > = ( { upsellPlanName } ) => {
const siteId = useSelector( getSelectedSiteId ) as number;
const gmtOffset = useSelector( ( state ) => getSiteGmtOffset( state, siteId ) );
const timezone = useSelector( ( state ) => getSiteTimezoneValue( state, siteId ) );
const currentDateDisplay = applySiteOffset( Date.now(), { gmtOffset, timezone } ).format( 'LT' );
const translate = useTranslate();

return (
<div className="activity-card-list__date-group-upsell">
<div className="activity-card-list__date-group-date">Past</div>
<div className="activity-card-list__date-group-content">
<div className="activity-card-list__secondary-card-with-more activity-card">
<div className="activity-card__header">
<div className="activity-card__time">
<Gridicon icon="cog" className="activity-card__time-icon" />
<div className="activity-card__time-text">{ currentDateDisplay }</div>
</div>
</div>
<Card>
<ActivityActor actorName="WordPress" actorType="Application" />
<div className="activity-card__activity-description">Description</div>
<div className="activity-card__activity-title">Title</div>
<div className="activity-card__activity-overlay">
<div className="activity-card__activity-overlay-content">
<div className="activity-card__activity-overlay-lock">
<Gridicon icon="lock" />
</div>
<p className="activity-card__activity-overlay-text">
{ translate(
'Upgrade to %(planName)s plan or higher to unlock more powerful features.',
{
args: {
planName: upsellPlanName,
},
comment: '%(planName)s is the name of the selected plan.',
}
) }
</p>
<button
type="button"
className="button activity-card__activity-overlay-button is-primary"
>
{ translate( 'Upgrade' ) }
</button>
</div>
</div>
</Card>
</div>
</div>
</div>
);
};

export default PlanUpsellCard;
169 changes: 169 additions & 0 deletions client/components/jetpack/upsell-product-wpcom-plan-card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import {
TERM_ANNUALLY,
getPlan,
getPlanFeaturesObject,
Plan,
PlanSlug,
isJetpackBackupSlug,
isJetpackScanSlug,
isJetpackSearchSlug,
} from '@automattic/calypso-products';
import { Gridicon } from '@automattic/components';
import { formatCurrency } from '@automattic/format-currency';
import { useTranslate } from 'i18n-calypso';
import { useMemo } from 'react';
import BackupImage from 'calypso/assets/images/jetpack/rna-image-backup.png';
import DefaultImage from 'calypso/assets/images/jetpack/rna-image-default.png';
import ScanImage from 'calypso/assets/images/jetpack/rna-image-scan.png';
import SearchImage from 'calypso/assets/images/jetpack/rna-image-search.png';
import DisplayPrice from 'calypso/components/jetpack/card/jetpack-product-card/display-price';
import JetpackRnaActionCard from 'calypso/components/jetpack/card/jetpack-rna-action-card';
import slugToSelectorProduct from 'calypso/my-sites/plans/jetpack-plans/slug-to-selector-product';
import { useSelector } from 'calypso/state';
import { getCurrentUserCurrencyCode } from 'calypso/state/currency-code/selectors';
import { getSiteAvailableProduct } from 'calypso/state/sites/products/selectors';
import { getSelectedSiteSlug } from 'calypso/state/ui/selectors';
import type { Duration, SelectorProduct } from 'calypso/my-sites/plans/jetpack-plans/types';

import './style.scss';

interface UpsellProductWpcomPlanCardProps {
nonManageProductSlug: string;
WPcomPlanSlug: PlanSlug;
siteId: number | null;
onCtaButtonClick: () => void;
}

export const UpsellProductWpcomPlanCard: React.FC< UpsellProductWpcomPlanCardProps > = ( {
nonManageProductSlug,
WPcomPlanSlug,
siteId,
onCtaButtonClick,
} ) => {
const translate = useTranslate();
const currencyCode = useSelector( getCurrentUserCurrencyCode );
const selectedSiteSlug = useSelector( getSelectedSiteSlug );
const item = slugToSelectorProduct( nonManageProductSlug ) as SelectorProduct;
const plan = getPlan( WPcomPlanSlug ) as Plan;
const planSlug = plan.getStoreSlug();
const jetpackFeatures = plan?.get2023PricingGridSignupJetpackFeatures?.();
const jetpackFeaturesObject = getPlanFeaturesObject( jetpackFeatures );
const siteProduct = useSelector( ( state ) =>
getSiteAvailableProduct( state, siteId, planSlug )
);

const billingTerm: Duration = TERM_ANNUALLY;
const ctaButtonURL: string = `https://wordpress.com/checkout/${ selectedSiteSlug }/${ planSlug }?redirect_to=${ encodeURIComponent(
window.location.href
) }`;
const isFetchingPrices: boolean = ! siteProduct;
const originalPrice: number = siteProduct?.cost_smallest_unit ?? 0;
const displayPrice: number = siteProduct?.cost / 12 ?? 0;

const ctaButtonLabel = translate( 'Get %(productName)s plan', {
args: {
productName: plan.getTitle(),
context: 'The Plan name, ie- Starter, Explorer, Creator,.',
},
} );

const upsellImageAlt = translate( 'Get %(productName)s plan', {
args: {
productName: plan.getTitle(),
context: 'The Plan name, ie- Starter, Explorer, Creator,.',
},
textOnly: true,
} );

const upsellImageUrl = useMemo( () => {
if ( isJetpackBackupSlug( nonManageProductSlug ) ) {
return BackupImage;
}
if ( isJetpackScanSlug( nonManageProductSlug ) ) {
return ScanImage;
}
if ( isJetpackSearchSlug( nonManageProductSlug ) ) {
return SearchImage;
}
return DefaultImage;
}, [ nonManageProductSlug ] );

const { displayName, description } = item;

const renderPriceDetails = () => {
return (
<>
<div className="display-price__details" aria-hidden="true">
<span className="display-price__billing-time-frame">
{ translate( 'per month, %(rawPrice)s billed annually, excl. taxes', {
args: {
rawPrice: ! isFetchingPrices
? formatCurrency( originalPrice ?? 0, currencyCode ?? '', {
stripZeros: true,
isSmallestUnit: true,
} )
: '',
},
comment: 'Excl. Taxes is short for excluding taxes',
} ) }
</span>
</div>
</>
);
};

const renderProductCardBody = () => {
return (
<>
<div className="upsell-product-card__price-plan">Creator plan</div>
<div className="upsell-product-card__price-container">
<DisplayPrice
isFree={ ! isFetchingPrices && originalPrice === 0 }
currencyCode={ currencyCode }
originalPrice={ displayPrice ?? 0 }
pricesAreFetching={ isFetchingPrices }
billingTerm={ billingTerm }
productName={ displayName }
hideSavingLabel={ false }
customTimeFrameBillingTerms={ renderPriceDetails() }
/>
</div>
<div className="upsell-product-card__included" aria-hidden="true">
{ translate( 'Included with %(planName)s plan:', {
args: {
planName: plan.getTitle(),
},
} ) }
</div>
<ul className="upsell-product-card__features is-wpcom-features">
{ jetpackFeaturesObject &&
jetpackFeaturesObject.map( ( feature, i ) => (
<li className="upsell-product-card__features-item" key={ i }>
<>
<Gridicon size={ 18 } icon="checkmark" /> { feature.getTitle() }
</>
</li>
) ) }
</ul>
</>
);
};

return (
<div className="upsell-product-wpcom-plan-card">
<JetpackRnaActionCard
headerText={ displayName }
subHeaderText={ description }
onCtaButtonClick={ onCtaButtonClick }
ctaButtonURL={ ctaButtonURL }
ctaButtonLabel={ ctaButtonLabel }
cardImage={ upsellImageUrl }
cardImageAlt={ upsellImageAlt }
>
{ renderProductCardBody() }
</JetpackRnaActionCard>
</div>
);
};

export default UpsellProductWpcomPlanCard;
Loading

0 comments on commit 702ad51

Please sign in to comment.