diff --git a/apps/odyssey-stats/src/widget/highlights.tsx b/apps/odyssey-stats/src/widget/highlights.tsx index 4c1f7a30d4baa0..bdc5b9eae44c74 100644 --- a/apps/odyssey-stats/src/widget/highlights.tsx +++ b/apps/odyssey-stats/src/widget/highlights.tsx @@ -1,7 +1,7 @@ -import { formattedNumber, SegmentedControl } from '@automattic/components'; +import { SegmentedControl } from '@automattic/components'; import { Icon, external } from '@wordpress/icons'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import moment from 'moment'; import { useState, FunctionComponent } from 'react'; import useReferrersQuery from '../hooks/use-referrers-query'; @@ -64,7 +64,9 @@ const ItemWrapper: FunctionComponent< ItemWrapperProps > = ( {

{ item.title }

{ translate( '%(views)s Views', { - args: { views: formattedNumber( item.views ) }, + args: { + views: numberFormat( item.views ), + }, } ) } diff --git a/apps/odyssey-stats/src/widget/modules.tsx b/apps/odyssey-stats/src/widget/modules.tsx index 961850df5d54ad..c98131b7fd8d4f 100644 --- a/apps/odyssey-stats/src/widget/modules.tsx +++ b/apps/odyssey-stats/src/widget/modules.tsx @@ -1,7 +1,7 @@ -import { ShortenedNumber, Button } from '@automattic/components'; +import { Button } from '@automattic/components'; import { protect, akismet } from '@automattic/components/src/icons'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import { useState, FunctionComponent } from 'react'; import wpcom from 'calypso/lib/wp'; import useModuleDataQuery from '../hooks/use-module-data-query'; @@ -64,7 +64,14 @@ const ModuleCard: FunctionComponent< ModuleCardProps > = ( { <> { ( ! isError || ! canManageModule ) && (
- + + { numberFormat( value, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } +
) } { isError && canManageModule && ( diff --git a/client/a8c-for-agencies/data/agencies/use-fetch-schedule-call-link.ts b/client/a8c-for-agencies/data/agencies/use-fetch-schedule-call-link.ts new file mode 100644 index 00000000000000..6ba26daf51ef63 --- /dev/null +++ b/client/a8c-for-agencies/data/agencies/use-fetch-schedule-call-link.ts @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query'; +import wpcom from 'calypso/lib/wp'; +import { useSelector } from 'calypso/state'; +import { getActiveAgencyId } from 'calypso/state/a8c-for-agencies/agency/selectors'; + +export default function useFetchScheduleCallLink() { + const agencyId = useSelector( getActiveAgencyId ); + + return useQuery( { + queryKey: [ 'agency-schedule-call-link', agencyId ], + queryFn: () => + wpcom.req.get( { + apiNamespace: 'wpcom/v2', + path: `/agency/${ agencyId }/schedule-call-link`, + } ), + enabled: false, + refetchOnWindowFocus: false, + } ); +} diff --git a/client/a8c-for-agencies/sections/agency-tier/download-badges/download-link.tsx b/client/a8c-for-agencies/sections/agency-tier/download-badges/download-link.tsx index d664289779cd53..fb6fad6957c4f4 100644 --- a/client/a8c-for-agencies/sections/agency-tier/download-badges/download-link.tsx +++ b/client/a8c-for-agencies/sections/agency-tier/download-badges/download-link.tsx @@ -38,12 +38,12 @@ function DownloadLink( { woocommerce: { 'agency-partner': { name: translate( 'Woo Agency Partner' ), - href: 'https://automattic.com/wp-content/uploads/2024/10/agency_tier_woo_partner.zip', + href: 'https://automattic.com/wp-content/uploads/2025/02/agency_tier_woo_partner.zip', icon: WooCommerce, }, 'pro-agency-partner': { name: translate( 'Woo Pro Agency Partner' ), - href: 'https://automattic.com/wp-content/uploads/2024/10/agency_tier_woo_pro_partner.zip', + href: 'https://automattic.com/wp-content/uploads/2025/02/agency_tier_woo_pro_partner.zip', icon: WooCommerce, }, }, diff --git a/client/a8c-for-agencies/sections/agency-tier/primary/agency-tier-overview/index.tsx b/client/a8c-for-agencies/sections/agency-tier/primary/agency-tier-overview/index.tsx index c69753225353ae..fe506e27387b66 100644 --- a/client/a8c-for-agencies/sections/agency-tier/primary/agency-tier-overview/index.tsx +++ b/client/a8c-for-agencies/sections/agency-tier/primary/agency-tier-overview/index.tsx @@ -34,15 +34,9 @@ export default function AgencyTierOverview() { const ALL_TIERS: AgencyTier[] = [ 'emerging-partner', 'agency-partner', 'pro-agency-partner' ]; - // todo: Restore this. We have to hide temporary the 'Download your badges' button until the WooCommerce ones are ready - // A4A GH issue: 1500 - const temporaryHideDownloadBadges = true; - // Show download badges button for Agency Partner and Pro Agency Partner tiers const showDownloadBadges = - ! temporaryHideDownloadBadges && - currentAgencyTier && - [ 'agency-partner', 'pro-agency-partner' ].includes( currentAgencyTier ); + currentAgencyTier && [ 'agency-partner', 'pro-agency-partner' ].includes( currentAgencyTier ); return ( diff --git a/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx b/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx index bc8d32c546b7f6..cb93e84e049a5c 100644 --- a/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx +++ b/client/a8c-for-agencies/sections/marketplace/hooks/use-product-and-plans.tsx @@ -81,14 +81,15 @@ const getDisplayableFeaturedProducts = ( filteredProductsAndBundles: APIProductFamilyProduct[] ) => { const featuredProductSlugs = [ - 'woocommerce-advanced-notifications', - 'jetpack-boost', - 'woocommerce-bulk-stock-management', + 'woocommerce-woopayments', + 'jetpack-security-t1', + 'woocommerce-subscriptions', ]; // For now, we hardcode this until we understand how we want pick featured products. - return filteredProductsAndBundles.filter( ( product ) => - featuredProductSlugs.includes( product.slug ) - ); + // We do it this way to ensure we follow the same order as the featuredProductSlugs. + return featuredProductSlugs + .map( ( slug ) => filteredProductsAndBundles.find( ( product ) => product.slug === slug ) ) + .filter( ( product ) => product !== undefined ) as APIProductFamilyProduct[]; }; const getDisplayableWoocommerceExtensions = ( diff --git a/client/a8c-for-agencies/sections/marketplace/hosting-overview-v3/style.scss b/client/a8c-for-agencies/sections/marketplace/hosting-overview-v3/style.scss index 95d997fb2f3c7a..726d3bb44c3a6b 100644 --- a/client/a8c-for-agencies/sections/marketplace/hosting-overview-v3/style.scss +++ b/client/a8c-for-agencies/sections/marketplace/hosting-overview-v3/style.scss @@ -41,7 +41,7 @@ .hosting-dashboard-layout__header-actions { background-color: var(--color-surface); border-radius: 8px; /* stylelint-disable-line */ - padding: 8px 16px; + padding: 8px 24px 8px 20px; .shopping-cart { margin: 0; diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview-v2/bundle-price-selector/index.tsx b/client/a8c-for-agencies/sections/marketplace/products-overview-v2/bundle-price-selector/index.tsx index 05ef1aad3fc4a9..7f8529bde7ad9d 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview-v2/bundle-price-selector/index.tsx +++ b/client/a8c-for-agencies/sections/marketplace/products-overview-v2/bundle-price-selector/index.tsx @@ -1,10 +1,6 @@ -import { Button } from '@wordpress/components'; -import { chevronDown } from '@wordpress/icons'; -import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; -import { useCallback, useRef, useState } from 'react'; -import PopoverMenu from 'calypso/components/popover-menu'; -import PopoverMenuItem from 'calypso/components/popover-menu/item'; +import { SelectControl } from '@wordpress/components'; +import { numberFormat, useTranslate } from 'i18n-calypso'; +import { useCallback } from 'react'; import './style.scss'; @@ -16,17 +12,6 @@ type Props = { export function BundlePriceSelector( { options, value, onChange }: Props ) { const translate = useTranslate(); - const [ isMenuOpen, setIsMenuOpen ] = useState( false ); - - const buttonRef = useRef( null ); - - const onSelect = useCallback( - ( option: number ) => { - setIsMenuOpen( false ); - onChange( option ); - }, - [ onChange ] - ); const getDiscountPercentage = useCallback( ( bundleSize: number ) => { @@ -64,43 +49,24 @@ export function BundlePriceSelector( { options, value, onChange }: Props ) { ); return ( - <> -
-
{ translate( 'Bundle & save' ) }
- -
- - setIsMenuOpen( false ) } - context={ buttonRef.current } - className="bundle-price-selector__popover" - autoPosition={ false } - position="bottom right" - > - { options.map( ( option ) => ( - onSelect( option ) } - key={ `bundle-price-option-${ option }` } - > - { getLabel( option ) } - - ) ) } - - + ( { + label: getLabel( option ) as string, + value: `${ numberFormat( option ) }`, + } ) ), + ] } + /> ); } diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview-v2/style.scss b/client/a8c-for-agencies/sections/marketplace/products-overview-v2/style.scss index 380754cd3c5da9..3403c51342bf11 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview-v2/style.scss +++ b/client/a8c-for-agencies/sections/marketplace/products-overview-v2/style.scss @@ -50,7 +50,7 @@ $action-panel-height-mobile: 128px; .hosting-dashboard-layout__header-actions { background-color: var(--color-surface); border-radius: 8px; /* stylelint-disable-line */ - padding: 8px 16px; + padding: 8px 24px 8px 20px; .shopping-cart { margin: 0; diff --git a/client/a8c-for-agencies/sections/marketplace/products-overview/product-filter/hooks/use-product-filter-options.tsx b/client/a8c-for-agencies/sections/marketplace/products-overview/product-filter/hooks/use-product-filter-options.tsx index f2e43b8bfa7e88..1362d4503cfdd3 100644 --- a/client/a8c-for-agencies/sections/marketplace/products-overview/product-filter/hooks/use-product-filter-options.tsx +++ b/client/a8c-for-agencies/sections/marketplace/products-overview/product-filter/hooks/use-product-filter-options.tsx @@ -59,6 +59,11 @@ export default function useProductFilterOptions() { }, ] : [] ), + { + key: PRODUCT_CATEGORY_PAYMENTS, + label: translate( 'Payments' ) as string, + icon: currencyDollar, + }, { key: PRODUCT_CATEGORY_SECURITY, label: translate( 'Security' ) as string, @@ -71,11 +76,6 @@ export default function useProductFilterOptions() { }, { key: PRODUCT_CATEGORY_SOCIAL, label: translate( 'Social' ) as string, icon: people }, { key: PRODUCT_CATEGORY_GROWTH, label: translate( 'Growth' ) as string, icon: trendingUp }, - { - key: PRODUCT_CATEGORY_PAYMENTS, - label: translate( 'Payments' ) as string, - icon: currencyDollar, - }, { key: PRODUCT_CATEGORY_SHIPPING_DELIVERY_FULFILLMENT, label: translate( 'Shipping, Delivery, and Fulfillment' ) as string, diff --git a/client/a8c-for-agencies/sections/overview/sidebar/growth-accelerator/index.tsx b/client/a8c-for-agencies/sections/overview/sidebar/growth-accelerator/index.tsx index cafc9ca73605b2..10043472061ce3 100644 --- a/client/a8c-for-agencies/sections/overview/sidebar/growth-accelerator/index.tsx +++ b/client/a8c-for-agencies/sections/overview/sidebar/growth-accelerator/index.tsx @@ -3,6 +3,7 @@ import { Button, Icon } from '@wordpress/components'; import { external } from '@wordpress/icons'; import { useTranslate } from 'i18n-calypso'; import { useCallback } from 'react'; +import useFetchScheduleCallLink from 'calypso/a8c-for-agencies/data/agencies/use-fetch-schedule-call-link'; import { useDispatch, useSelector } from 'calypso/state'; import { recordTracksEvent } from 'calypso/state/analytics/actions'; import { savePreference } from 'calypso/state/preferences/actions'; @@ -18,10 +19,22 @@ export default function OverviewSidebarGrowthAccelerator() { const dispatch = useDispatch(); + const { refetch: fetchScheduleCallLink, isFetching: isFetchingScheduleCallLink } = + useFetchScheduleCallLink(); + const onRequestCallClick = useCallback( () => { dispatch( recordTracksEvent( 'calypso_a4a_overview_growth_accelerator_schedule_call_click' ) ); dispatch( savePreference( GROWTH_ACCELERATOR_REQUESTED_PREFERENCE, true ) ); - }, [ dispatch ] ); + + fetchScheduleCallLink().then( ( result ) => { + window.open( + result.data + ? result.data + : 'https://savvycal.com/automattic-for-agencies/agency-success?utm_campaign=overview', + '_blank' + ); + } ); + }, [ dispatch, fetchScheduleCallLink ] ); const onNotInterestedClick = useCallback( () => { dispatch( recordTracksEvent( 'calypso_a4a_overview_growth_accelerator_not_interested_click' ) ); @@ -56,9 +69,8 @@ export default function OverviewSidebarGrowthAccelerator() { + + + + ); +}; + +export default BlueprintForm; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/index.tsx new file mode 100644 index 00000000000000..af652a7f21b88d --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/index.tsx @@ -0,0 +1,40 @@ +import { Button } from '@wordpress/components'; +import { useTranslate } from 'i18n-calypso'; +import Form from 'calypso/a8c-for-agencies/components/form'; +import { preventWidows } from 'calypso/lib/formatting'; + +import './style.scss'; + +type Props = { + onContinue: () => void; + onSkip: () => void; +}; + +const ChoiceBlueprint: React.FC< Props > = ( { onContinue, onSkip } ) => { + const translate = useTranslate(); + + return ( +
+

{ translate( 'Ready?' ) }

+ +
+ + +
+
+ ); +}; + +export default ChoiceBlueprint; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/style.scss new file mode 100644 index 00000000000000..cf82a9f7a325e9 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/style.scss @@ -0,0 +1,16 @@ +.choice-blueprint__text { + font-size: 1rem; + font-weight: 400; + color: var(--color-neutral-60); + max-width: 460px; +} + +.choice-blueprint__buttons { + display: flex; + gap: 24px; + + .components-button { + width: 180px; + justify-content: center; + } +} \ No newline at end of file diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/index.tsx new file mode 100644 index 00000000000000..a3571b9612a808 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/index.tsx @@ -0,0 +1,162 @@ +import { Gridicon } from '@automattic/components'; +import { localizeUrl } from '@automattic/i18n-utils'; +import { Button } from '@wordpress/components'; +import { useTranslate } from 'i18n-calypso'; +import { useState } from 'react'; +import Form from 'calypso/a8c-for-agencies/components/form'; +import FormField from 'calypso/a8c-for-agencies/components/form/field'; +import QuerySmsCountries from 'calypso/components/data/query-countries/sms'; +import FormPhoneInput from 'calypso/components/forms/form-phone-input'; +import FormTextInput from 'calypso/components/forms/form-text-input'; +import { useGetSupportedSMSCountries } from 'calypso/jetpack-cloud/sections/agency-dashboard/downtime-monitoring/contact-editor/hooks'; + +import './style.scss'; + +type FormData = { + firstName: string; + lastName: string; + email: string; + agencyName: string; + businessUrl: string; + phoneNumber: string; +}; + +type Props = { + onContinue: () => void; +}; + +const SignupContactForm = ( { onContinue }: Props ) => { + const translate = useTranslate(); + + const countriesList = useGetSupportedSMSCountries(); + const noCountryList = countriesList.length === 0; + + const [ formData, setFormData ] = useState< FormData >( { + firstName: '', + lastName: '', + email: '', + agencyName: '', + businessUrl: '', + phoneNumber: '', + } ); + + const handlePhoneInputChange = ( data: { phoneNumberFull: string } ) => { + setFormData( ( prev ) => ( { + ...prev, + phoneNumber: data.phoneNumberFull, + } ) ); + }; + + const handleInputChange = + ( field: keyof FormData ) => ( event: React.ChangeEvent< HTMLInputElement > ) => { + setFormData( ( prev ) => ( { + ...prev, + [ field ]: event.target.value, + } ) ); + }; + + const handleSubmit = ( e: React.FormEvent ) => { + e.preventDefault(); + onContinue(); + }; + + return ( +
+
+
+ + + + + + + +
+ + + + + + + + + + + + + + { noCountryList && } + + + + + +
+

+ { translate( + "By clicking 'Continue', you agree to the{{break}}{{/break}}{{link}}Terms of the Automattic for Agencies Platform Agreement{{icon}}{{/icon}}{{/link}}.", + { + components: { + break:
, + link: ( + + ), + icon: , + }, + } + ) } +

+
+
+ +
+
+
+ ); +}; + +export default SignupContactForm; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/style.scss new file mode 100644 index 00000000000000..3062539f37da99 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/style.scss @@ -0,0 +1,99 @@ +.signup-multi-step-form__fields { + display: flex; + flex-direction: column; + gap: 8px; + overflow-y: auto; + flex: 1; +} + +.contact-form__phone-input { + display: flex; + flex-direction: row; + gap: 1rem; + + .form-phone-input__country { + flex: 1; + } + + .form-phone-input__phone-number { + flex: 1; + } +} + +.signup-contact-form__tos { + p { + font-size: 0.875rem; + } +} + +.signup-multi-step-form__name-fields { + display: flex; + flex-direction: row; + gap: 24px; + + div { + flex-grow: 1; + } +} + +.signup-multi-step-form__terms { + text-align: center; + font-size: 0.875rem; + color: var(--color-neutral-40); + margin-top: 16px; + + a { + color: var(--studio-wordpress-blue); + text-decoration: none; + } + + .signup-multi-step-form__terms a:hover { + text-decoration: underline; + } +} + +// Form field styles +.a4a-form__section-field { + margin-bottom: 0; + padding: 2px; +} + +.a4a-form__section-field-required { + color: var(--studio-red-50); + margin-left: 4px; +} + +.a4a-form__section-field-optional { + color: var(--color-neutral-20); + font-weight: normal; + margin-left: 4px; +} + +.form-text-input { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--color-neutral-10); + border-radius: 4px; + font-size: 1rem; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.form-text-input:focus { + border-color: var(--studio-wordpress-blue); + box-shadow: 0 0 0 2px var(--studio-wordpress-blue-20); + outline: none; +} + +.form-text-input.is-error { + border-color: var(--studio-red-50); +} + +.a4a-form__error { + color: var(--studio-red-50); + font-size: 0.75rem; + margin-top: 4px; +} + +.a4a-form__error.hidden { + display: none; +} diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/finish-signup-survey/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/finish-signup-survey/index.tsx new file mode 100644 index 00000000000000..2cc753a86ee98f --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/finish-signup-survey/index.tsx @@ -0,0 +1,28 @@ +import { Button } from '@wordpress/components'; +import { useTranslate } from 'i18n-calypso'; +import Form from 'calypso/a8c-for-agencies/components/form'; + +type Props = { + onContinue: () => void; +}; + +const FinishSignupSurvey: React.FC< Props > = ( { onContinue } ) => { + const translate = useTranslate(); + + return ( +
+
+ +
+
+ ); +}; + +export default FinishSignupSurvey; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/index.tsx new file mode 100644 index 00000000000000..a3431361977640 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/index.tsx @@ -0,0 +1,62 @@ +import { useTranslate } from 'i18n-calypso'; +import { useMemo, useState } from 'react'; +import StepProgress from '../step-progress'; +import BlueprintForm from './blueprint-form'; +import ChoiceBlueprint from './choice-blueprint'; +import SignupContactForm from './contact-form'; +import FinishSignupSurvey from './finish-signup-survey'; +import PersonalizationForm from './personalization'; +import './style.scss'; + +const MultiStepForm = () => { + const translate = useTranslate(); + const [ currentStep, setCurrentStep ] = useState( 1 ); + + const steps = [ + { label: translate( 'Sign up' ), isActive: currentStep === 1, isComplete: currentStep > 1 }, + { + label: translate( 'Personalize' ), + isActive: currentStep === 2 || currentStep === 3 || currentStep === 4, + isComplete: currentStep > 2, + }, + { + label: translate( 'Finish survey' ), + isActive: currentStep === 5, + isComplete: currentStep > 5, + }, + ]; + + const currentForm = useMemo( () => { + switch ( currentStep ) { + case 1: + return setCurrentStep( 2 ) } />; + case 2: + return setCurrentStep( 3 ) } />; + case 3: + return ( + setCurrentStep( 4 ) } + onSkip={ () => setCurrentStep( 5 ) } + /> + ); + case 4: + return setCurrentStep( 5 ) } />; + case 5: + return setCurrentStep( 6 ) } />; + case 6: + return

Thank you!

; + default: + return null; + } + }, [ currentStep ] ); + + return ( +
+ + + { currentForm } +
+ ); +}; + +export default MultiStepForm; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/index.tsx new file mode 100644 index 00000000000000..91a11bd83757a9 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/index.tsx @@ -0,0 +1,170 @@ +import { SearchableDropdown } from '@automattic/components'; +import { Button } from '@wordpress/components'; +import { useTranslate } from 'i18n-calypso'; +import { useState, ChangeEvent } from 'react'; +import Form from 'calypso/a8c-for-agencies/components/form'; +import FormField from 'calypso/a8c-for-agencies/components/form/field'; +import { useCountriesAndStates } from 'calypso/a8c-for-agencies/sections/signup/agency-details-form/hooks/use-countries-and-states'; +import FormFieldset from 'calypso/components/forms/form-fieldset'; +import FormSelect from 'calypso/components/forms/form-select'; +import MultiCheckbox from 'calypso/components/forms/multi-checkbox'; + +import './style.scss'; + +interface Props { + onContinue: () => void; +} + +export default function PersonalizationForm( { onContinue }: Props ) { + const translate = useTranslate(); + const { countryOptions } = useCountriesAndStates(); + + const [ country, setCountry ] = useState( '' ); + const [ userType, setUserType ] = useState( '' ); + const [ managedSites, setManagedSites ] = useState( '' ); + const [ servicesOffered, setServicesOffered ] = useState< string[] >( [] ); + const [ productsOffered, setProductsOffered ] = useState< string[] >( [] ); + + const handleSetServicesOffered = ( services: { value: string[] } ) => { + setServicesOffered( services.value ); + }; + + const handleSetProductsOffered = ( products: { value: string[] } ) => { + setProductsOffered( products.value ); + }; + + const handleSubmit = async ( e: React.FormEvent ) => { + e.preventDefault(); + + onContinue(); + }; + + return ( +
+
+
+ + + { + setCountry( value ?? '' ); + } } + options={ countryOptions } + label={ translate( 'Select country' ) } + /> + + + + + + ) => { + setUserType( e.target.value ); + } } + > + + + + + + + + + + + + + ) => { + setManagedSites( e.target.value ); + } } + > + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+ ); +} diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/style.scss new file mode 100644 index 00000000000000..cdabca47d95dd2 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/style.scss @@ -0,0 +1,91 @@ +.signup-personalization-form { + .signup-multi-step-form__fields { + display: flex; + flex-direction: column; + overflow-y: auto; + flex: 1; + } + + .signup-personalization-form__products-checkbox { + .multi-checkbox label { + flex: 0 1 calc(32% - 16px); // 3 columns for products + + @media (max-width: 782px) { + flex: 0 1 calc(50% - 12px); // 2 columns on tablets + } + + @media (max-width: 600px) { + flex: 0 1 100%; // Full width on mobile + } + } + } + + .form-fieldset { + margin: 0; + padding: 2px; + } + + // Form field styles + .a4a-form__section-field { + margin-bottom: 0; + } + + .a4a-form__section-field-label { + font-size: rem(14px); + font-weight: 600; + color: var(--color-neutral-80); + margin-bottom: 8px; + } + + .a4a-form__section-field-required { + color: var(--studio-red-50); + margin-left: 4px; + } + + .form-select { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--color-neutral-10); + border-radius: 4px; + font-size: 1rem; + transition: border-color 0.2s ease, box-shadow 0.2s ease; + } + + .form-select:focus { + border-color: var(--studio-wordpress-blue); + box-shadow: 0 0 0 2px var(--studio-wordpress-blue-20); + outline: none; + } + + .form-select.is-error { + border-color: var(--studio-red-50); + } + + .searchable-dropdown { + width: 100%; + } + + .multi-checkbox { + display: flex; + flex-wrap: wrap; + gap: 12px 24px; + } + + .multi-checkbox label { + display: flex; + align-items: center; + margin: 0; + cursor: pointer; + font-size: rem(14px); + color: var(--color-neutral-80); + flex: 0 1 calc(50% - 12px); // 2 columns by default, accounting for gap + + @media (max-width: 600px) { + flex: 0 1 100%; // Full width on mobile + } + } + + .multi-checkbox .form-checkbox + span { + margin-left: 8px; + } +} \ No newline at end of file diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/style.scss new file mode 100644 index 00000000000000..cd86ad82f1728b --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/style.scss @@ -0,0 +1,24 @@ +.signup-multi-step-form { + .a4a-form { + padding: 48px 24px; + max-width: 600px; + width: 100%; + gap: 16px; + } + + .a4a-form__heading { + .a4a-form__heading-title { + font-size: 2rem; + font-weight: 600; + margin-bottom: 16px; + color: var(--color-neutral-100); + } + + .a4a-form__heading-description { + font-size: 1rem; + font-weight: 400; + color: var(--color-neutral-60); + max-width: 460px; + } + } +} diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/index.tsx index 0ba9351a6a7685..54431c8cddb7b7 100644 --- a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/index.tsx +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/index.tsx @@ -5,6 +5,7 @@ import A4ALogo, { import { useIsDarkMode } from 'calypso/a8c-for-agencies/hooks/use-is-dark-mode'; import SignupSidebarImage from './sidebar-image'; import SignupTestimonial from './testimonial'; + import './style.scss'; type Props = { diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/sidebar-image.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/sidebar-image.tsx index 452008c94e19af..79ffb88a28de8e 100644 --- a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/sidebar-image.tsx +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/sidebar-image.tsx @@ -7,21 +7,37 @@ const SignupSidebarImage = ( { className }: Props ) => { return ( - + - + + + + + + + ); }; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/style.scss index 0e99465a17e48d..6cedab317d75b2 100644 --- a/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/style.scss +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/signup-wrapper/style.scss @@ -9,7 +9,7 @@ margin: 16px; border-radius: 16px; //stylelint-disable-line scales/radii - &__left { + .signup-wrapper__left { display: none; position: relative; background: var(--color-neutral-0); @@ -27,11 +27,11 @@ } } - &__logo { + .signup-wrapper__logo { margin-block-start: 1rem; } - &__sidebar-image { + .signup-wrapper__sidebar-image { position: relative; left: 50%; transform: translateX(-50%); @@ -39,12 +39,12 @@ max-width: 100%; height: auto; - circle { - stroke: var(--color-neutral-0); + rect { + fill: var(--color-neutral-0); } } - &__testimonial { + .signup-wrapper-testimonial { color: var(--color-text); padding: 24px; background: var(--color-surface); @@ -53,111 +53,143 @@ position: relative; top: -1rem; - &-text { + .signup-wrapper-testimonial-text { font-size: 1rem; line-height: 1.5; margin-bottom: 24px; color: var(--color-text); } - &-author { + .signup-wrapper-testimonial-author { display: flex; align-items: center; gap: 12px; } - &-avatar { + .signup-wrapper-testimonial-avatar { width: 48px; height: 48px; border-radius: 50%; background: var(--color-neutral-5); } - &-info { + .signup-wrapper-testimonial-info { display: flex; flex-direction: column; gap: 4px; } - &-name { + .signup-wrapper-testimonial-name { font-weight: 600; font-size: 1rem; color: var(--color-text); } - &-title { - font-weight: 600; - font-size: 1rem; - color: var(--color-text); + .signup-wrapper-testimonial-title { + font-weight: 600; + font-size: 1rem; + color: var(--color-text); } - &-url { + .signup-wrapper-testimonial-url { font-size: 0.875rem; - color: var(--color-text-subtle);; + color: var(--color-text-subtle); text-decoration: underline; - &:visited { - color: var(--color-text-subtle);; + .signup-wrapper-testimonial-url:visited { + color: var(--color-text-subtle); } } } - &__right { + .signup-wrapper__right { flex: 1; padding: 32px; background: inherit; border-radius: 4px; - overflow: hidden; + overflow-y: auto; + max-height: 100%; + scrollbar-width: thin; + scrollbar-gutter: stable; + + /* Custom scrollbar styling for webkit browsers */ + &::-webkit-scrollbar { + width: 8px; + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: var(--color-neutral-10); + border-radius: 4px; + + &:hover { + background-color: var(--color-neutral-20); + } + } + + &::-webkit-scrollbar-track { + background-color: transparent; + } @include break-medium { - padding: 48px; + padding: 48px 48px 48px 32px; } } @media (prefers-color-scheme: dark) { background-color: #021B24; - &__left { + .signup-wrapper__left { background: #022836; } - &__logo { - fill: var(--studio-white); + .signup-wrapper__logo { + fill: var(--color-surface); } - &__sidebar-image { - circle { - stroke: #022836; + .signup-wrapper__sidebar-image { + rect { + fill: #022836; } } - &__testimonial { - color: var(--studio-white); + .signup-wrapper-testimonial { + color: var(--color-surface); background: #02506E; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); backdrop-filter: blur(8px); - &-text { - color: var(--studio-gray-0, #f6f7f7); + .signup-wrapper-testimonial-text { + color: var(--color-neutral-0); } - &-avatar { - background: var(--studio-gray-50, #646970); + .signup-wrapper-testimonial-avatar { + background: var(--color-neutral-50); } - &-name { - color: var(--studio-white); + .signup-wrapper-testimonial-name { + color: var(--color-surface); } - &-title { - color: var(--studio-white); + .signup-wrapper-testimonial-title { + color: var(--color-surface); } - &-url { - color: var(--studio-white); + .signup-wrapper-testimonial-url { + color: var(--color-surface); &:visited { - color: var(--studio-white); + color: var(--color-surface); + } + } + } + + .signup-wrapper__right { + &::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.2); + + &:hover { + background-color: rgba(255, 255, 255, 0.3); } } } diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/index.tsx new file mode 100644 index 00000000000000..ab58f3add17c2a --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/index.tsx @@ -0,0 +1,38 @@ +import clsx from 'clsx'; + +import './style.scss'; + +type Step = { + label: string; + isActive: boolean; + isComplete: boolean; +}; + +type Props = { + steps: Step[]; +}; + +const StepProgress = ( { steps }: Props ) => { + return ( +
+
+
+ { steps.map( ( step ) => ( +
+
+ { step.label } +
+ ) ) } +
+
+
+ ); +}; + +export default StepProgress; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/style.scss b/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/style.scss new file mode 100644 index 00000000000000..0352975c3bd11f --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/style.scss @@ -0,0 +1,73 @@ +.step-progress { + width: 100%; + background: var(--studio-white); + + .step-progress__steps { + max-width: 600px; + margin: 0 auto; + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + } + + .step-progress__steps-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + width: 100%; + } + + .step-progress__step { + display: flex; + flex-direction: column; + gap: 8px; + } + + .step-progress__step.is-active { + .step-progress__step-indicator { + background-color: var(--color-primary-50); + } + + .step-progress__step-label { + color: var(--color-neutral-80); + font-weight: 600; + } + } + + .step-progress__step.is-complete { + .step-progress__step-indicator { + background-color: var(--color-primary-50); + } + + .step-progress__step-label { + color: var(--color-neutral-80); + font-weight: 600; + } + } + + .step-progress__step:not(.is-active):not(.is-complete) { + .step-progress__step-indicator { + background-color: var(--color-neutral-5); + } + + .step-progress__step-label { + color: var(--color-neutral-40); + } + } + + .step-progress__step-indicator { + height: 8px; + width: 100%; + border-radius: 4px; + transition: background-color 0.2s ease; + } + + .step-progress__step-label { + font-size: 0.875rem; + font-weight: 500; + transition: color 0.2s ease; + order: -1; + margin-bottom: 4px; + } +} \ No newline at end of file diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/index.tsx index ab58a9fb57bb29..963a85b6ab5410 100644 --- a/client/a8c-for-agencies/sections/signup/signup-v2/index.tsx +++ b/client/a8c-for-agencies/sections/signup/signup-v2/index.tsx @@ -1,15 +1,10 @@ +import MultiStepForm from './components/multi-step-form'; import SignupWrapper from './components/signup-wrapper'; -type Flow = 'regular' | 'wc-asia'; - -type Props = { - flow?: Flow; -}; - -const AgencySignupV2 = ( { flow }: Props ) => { +const AgencySignupV2 = () => { return ( -
Agency Signup V2: { flow }
+
); }; diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/types.ts b/client/a8c-for-agencies/sections/signup/signup-v2/types.ts new file mode 100644 index 00000000000000..64c508fd0cbd21 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/types.ts @@ -0,0 +1 @@ +export type Flow = 'regular' | 'wc-asia'; diff --git a/client/a8c-for-agencies/style.scss b/client/a8c-for-agencies/style.scss index 1b4130a11e3309..b9feebca59f466 100644 --- a/client/a8c-for-agencies/style.scss +++ b/client/a8c-for-agencies/style.scss @@ -166,7 +166,7 @@ .components-button.is-primary, .button.is-primary { - &, + &:not( :disabled ), &:focus:not(:disabled) { background-color: var(--color-primary-50); border-color: var(--color-primary-50); diff --git a/client/assets/images/jetpack/rna-image-complete.png b/client/assets/images/jetpack/rna-image-complete.png deleted file mode 100644 index 4703bed0cc7012..00000000000000 Binary files a/client/assets/images/jetpack/rna-image-complete.png and /dev/null differ diff --git a/client/assets/images/jetpack/rna-image-complete@2x.png b/client/assets/images/jetpack/rna-image-complete@2x.png deleted file mode 100644 index 4dd3eee6ce7c8b..00000000000000 Binary files a/client/assets/images/jetpack/rna-image-complete@2x.png and /dev/null differ diff --git a/client/components/jetpack/card/jetpack-rna-dialog-card/index.tsx b/client/components/jetpack/card/jetpack-rna-dialog-card/index.tsx deleted file mode 100644 index 34c6ff107d49cb..00000000000000 --- a/client/components/jetpack/card/jetpack-rna-dialog-card/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import clsx from 'clsx'; -import { ReactNode } from 'react'; -import DefaultBackgroundImage from 'calypso/assets/images/jetpack/rna-card-bg.png'; - -import './style.scss'; - -interface RnaDialogCardProps { - children: ReactNode; - cardImage?: string; - cardImage2xRetina?: string; - isPlaceholder?: boolean; -} - -const JetpackRnaDialogCard: React.FC< RnaDialogCardProps > = ( { - children, - cardImage, - cardImage2xRetina, - isPlaceholder, -} ) => { - const cardDesktopSideImage = - window.devicePixelRatio > 1 && cardImage2xRetina - ? cardImage2xRetina - : cardImage ?? DefaultBackgroundImage; - return ( -
-
-
{ children }
-
-
-
- ); -}; - -export default JetpackRnaDialogCard; diff --git a/client/components/jetpack/card/jetpack-rna-dialog-card/style.scss b/client/components/jetpack/card/jetpack-rna-dialog-card/style.scss deleted file mode 100644 index 608c3735e1eb03..00000000000000 --- a/client/components/jetpack/card/jetpack-rna-dialog-card/style.scss +++ /dev/null @@ -1,57 +0,0 @@ -@import "@automattic/typography/styles/variables"; -@import "@automattic/onboarding/styles/mixins"; -@import "@wordpress/base-styles/breakpoints"; - -.jetpack-rna-dialog-card { - box-sizing: content-box; - background-color: var(--studio-white); - width: 100%; - margin-top: 16px; - border: 1px solid var(--studio-gray-5); - border-radius: 4px; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: space-between; - @include break-large { - flex-direction: row; - } - &.is-placeholder { - @include placeholder( --studio-gray-5 ); - border: none; - } - - &__body { - display: flex; - flex-direction: column; - justify-content: space-between; - min-width: 238px; - padding: 48px 32px; - box-sizing: border-box; - color: var(--studio-black); - @include break-large { - flex: 0 55 669px; - padding: 64px; - } - } - - &__footer { - order: -1; - background-repeat: no-repeat; - background-size: cover; - background-position: 50% center; - @include break-large { - order: initial; - flex: 0 46 460px; - display: flex; - align-items: center; - } - } - - &__placeholder-image { - height: 0; - margin: 25px 65px 25px 25px; - padding-bottom: 85%; - - } -} diff --git a/client/components/jetpack/jetpack-product-info/section.tsx b/client/components/jetpack/jetpack-product-info/section.tsx index d889037e2f65cb..bde94a9f19cb1d 100644 --- a/client/components/jetpack/jetpack-product-info/section.tsx +++ b/client/components/jetpack/jetpack-product-info/section.tsx @@ -40,6 +40,7 @@ const JetpackProductInfoSection: FunctionComponent< Props > = ( { clickableHeader smooth contentExpandedStyle={ contentStyle } + expanded >
{ children }
diff --git a/client/controller/index.web.js b/client/controller/index.web.js index 0c0d1232ff5785..0b939c790e90b0 100644 --- a/client/controller/index.web.js +++ b/client/controller/index.web.js @@ -411,7 +411,7 @@ export const redirectIfDuplicatedView = ( wpAdminPath ) => async ( context, next const overrideAssignment = getPreference( context.store.getState(), - 'remove_duplicate_views_experiment_assignment' + 'remove_duplicate_views_experiment_assignment_160125' ); if ( 'control' === overrideAssignment ) { diff --git a/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-expanded-content/insights-stats.tsx b/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-expanded-content/insights-stats.tsx index 03478eaa893da9..4bae6bdc083922 100644 --- a/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-expanded-content/insights-stats.tsx +++ b/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-expanded-content/insights-stats.tsx @@ -1,7 +1,7 @@ -import { Button, ShortenedNumber } from '@automattic/components'; +import { Button } from '@automattic/components'; import { Icon, arrowUp, arrowDown, external } from '@wordpress/icons'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import ExpandedCard from './expanded-card'; import type { SiteStats } from '../types'; @@ -62,7 +62,14 @@ export default function InsightsStats( { stats, siteUrlWithScheme, trackEvent }:
- + + { numberFormat( data.visitors, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } + { getTrendContent( data.visitorsTrend, data.visitorsChange ) }
@@ -71,7 +78,14 @@ export default function InsightsStats( { stats, siteUrlWithScheme, trackEvent }:
- + + { numberFormat( data.views, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } + { getTrendContent( data.viewsTrend, data.viewsChange ) }
diff --git a/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-status-content/site-stats-column.tsx b/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-status-content/site-stats-column.tsx index 25e934fdbca55c..46f3b3163a559e 100644 --- a/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-status-content/site-stats-column.tsx +++ b/client/jetpack-cloud/sections/agency-dashboard/sites-overview/site-status-content/site-stats-column.tsx @@ -1,4 +1,4 @@ -import { Gridicon, ShortenedNumber } from '@automattic/components'; +import { Gridicon } from '@automattic/components'; import { Icon, arrowUp, arrowDown } from '@wordpress/icons'; import clsx from 'clsx'; import { translate, numberFormat } from 'i18n-calypso'; @@ -109,7 +109,14 @@ export default function SiteStatsColumn( { site, stats, siteError }: Props ) { { trendIcon }
- + + { numberFormat( totalViews, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } +
); diff --git a/client/jetpack-connect/authorize.js b/client/jetpack-connect/authorize.js index 1dcd9e73acd6f1..dbd884ff4fe760 100644 --- a/client/jetpack-connect/authorize.js +++ b/client/jetpack-connect/authorize.js @@ -73,7 +73,6 @@ import { JPC_A4A_PATH, JPC_JETPACK_MANAGE_PATH, JPC_PATH_PLANS, - JPC_PATH_PLANS_COMPLETE, REMOTE_PATH_AUTH, } from './constants'; import Disclaimer from './disclaimer'; @@ -995,19 +994,6 @@ export class JetpackAuthorize extends Component { return redirectAfterAuth; } - // We naviage users to complete page if it's not a multisite and feature flag activated - if ( config.isEnabled( 'jetpack/offer-complete-after-activation' ) ) { - const isMultisite = this.props.site && this.props.site.is_multisite; - if ( ! isMultisite && this.props.site ) { - const jpcTargetComplete = `${ JPC_PATH_PLANS_COMPLETE }/${ urlToSlug( homeUrl ) }`; - debug( - 'authorization-form: getRedirectionTarget -> Redirection target is: %s', - jpcTargetComplete - ); - return jpcTargetComplete; - } - } - const jpcTarget = addQueryArgs( { redirect_to: redirectAfterAuth }, `${ JPC_PATH_PLANS }/${ urlToSlug( homeUrl ) }` diff --git a/client/jetpack-connect/constants.js b/client/jetpack-connect/constants.js index d6f8a7a2a5b2b9..62e2e46d8fa3d3 100644 --- a/client/jetpack-connect/constants.js +++ b/client/jetpack-connect/constants.js @@ -6,7 +6,6 @@ export const JETPACK_ADMIN_PATH = '/wp-admin/admin.php?page=jetpack'; export const JETPACK_MINIMUM_WORDPRESS_VERSION = '4.7'; export const JPC_PATH_BASE = '/jetpack/connect'; export const JPC_PATH_PLANS = '/jetpack/connect/plans'; -export const JPC_PATH_PLANS_COMPLETE = '/jetpack/connect/plans/complete'; export const JPC_PATH_REMOTE_INSTALL = '/jetpack/connect/install'; export const MINIMUM_JETPACK_VERSION = '3.9.6'; export const REMOTE_PATH_ACTIVATE = '/wp-admin/plugins.php'; diff --git a/client/jetpack-connect/index.js b/client/jetpack-connect/index.js index cbe43c7a694246..cbf44811745be4 100644 --- a/client/jetpack-connect/index.js +++ b/client/jetpack-connect/index.js @@ -103,6 +103,11 @@ export default function () { controller.offerResetContext ); + // Redirect /jetpack/connect/plans/complete/* to /jetpack/connect/plans + // See https://github.com/Automattic/wp-calypso/pull/74558 + page( '/jetpack/connect/plans/complete', '/jetpack/connect/plans' ); + page( '/jetpack/connect/plans/complete/*', '/jetpack/connect/plans' ); + page( '/jetpack/sso/:siteId?/:ssoNonce?', controller.sso, makeLayout, clientRender ); page( '/jetpack/sso/*', controller.sso, makeLayout, clientRender ); diff --git a/client/jetpack-connect/test/authorize.js b/client/jetpack-connect/test/authorize.js index 9801bc6e70a45d..f7da5b2cb512e3 100644 --- a/client/jetpack-connect/test/authorize.js +++ b/client/jetpack-connect/test/authorize.js @@ -12,7 +12,7 @@ import siteConnectionReducer from 'calypso/state/site-connection/reducer'; import uiReducer from 'calypso/state/ui/reducer'; import { renderWithProvider } from '../../../client/test-helpers/testing-library'; import { JetpackAuthorize } from '../authorize'; -import { JPC_PATH_PLANS, JPC_PATH_PLANS_COMPLETE } from '../constants'; +import { JPC_PATH_PLANS } from '../constants'; import { OFFER_RESET_FLOW_TYPES } from '../flow-types'; const noop = () => {}; @@ -474,55 +474,6 @@ describe( 'JetpackAuthorize', () => { expect.any( String ) ); } ); - - test( 'should redirect to the /jetpack/connect/plans when feature flag disabled and not multisite', async () => { - config.isEnabled.mockImplementation( - ( flag ) => flag !== 'jetpack/offer-complete-after-activation' - ); - renderWithRedux( - - ); - - await userEvent.click( screen.getByText( 'Return to your site' ) ); - - expect( windowOpenSpy ).toHaveBeenCalledWith( - `${ JPC_PATH_PLANS }/${ SITE_SLUG }?redirect_to=${ encodeURIComponent( - DEFAULT_PROPS.authQuery.redirectAfterAuth - ) }`, - expect.any( String ) - ); - } ); - - test( 'should redirect to the /jetpack/connect/plans/complete when feature flag enabled and not multisite', async () => { - renderWithRedux( - - ); - - await userEvent.click( screen.getByText( 'Return to your site' ) ); - - expect( windowOpenSpy ).toHaveBeenCalledWith( - `${ JPC_PATH_PLANS_COMPLETE }/${ SITE_SLUG }`, - expect.any( String ) - ); - } ); } ); describe( 'handleSignIn', () => { diff --git a/client/landing/stepper/declarative-flow/internals/components/stepper-loader/index.tsx b/client/landing/stepper/declarative-flow/internals/components/stepper-loader/index.tsx index 4ef75cfff684d2..cfe067f78db282 100644 --- a/client/landing/stepper/declarative-flow/internals/components/stepper-loader/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/components/stepper-loader/index.tsx @@ -1,10 +1,27 @@ +import { ProgressBar } from '@wordpress/components'; import clsx from 'clsx'; -import WordPressLogo from 'calypso/components/wordpress-logo'; import './style.scss'; -const StepperLoader = () => { - /* eslint-disable wpcalypso/jsx-classname-namespace */ - return ; +interface StepperLoaderProps { + title?: string; + subtitle?: React.ReactNode; + progress?: number; + className?: string; +} + +const StepperLoader: React.FC< StepperLoaderProps > = ( { + title, + subtitle, + progress, + className, +} ) => { + return ( +
+

{ title }

+ + { subtitle &&

{ subtitle }

} +
+ ); }; export default StepperLoader; diff --git a/client/landing/stepper/declarative-flow/internals/components/stepper-loader/style.scss b/client/landing/stepper/declarative-flow/internals/components/stepper-loader/style.scss index c1832c29161b27..aa3cbb65e801d6 100644 --- a/client/landing/stepper/declarative-flow/internals/components/stepper-loader/style.scss +++ b/client/landing/stepper/declarative-flow/internals/components/stepper-loader/style.scss @@ -1,3 +1,43 @@ +@import "@wordpress/base-styles/breakpoints"; +@import "@wordpress/base-styles/mixins"; +@import "@automattic/onboarding/styles/mixins"; + .stepper-loader { - animation: loading-fade 1.6s ease-in-out infinite; + --wp-components-color-foreground: #3858e9; + padding: 1em; + max-width: 540px; + margin: 32vh auto 0; + + &.stepper-loader__boot { + margin-top: calc(32vh + 60px); + } + + .stepper-loader__title { + @include onboarding-font-recoleta; + font-weight: 400; + height: 40px; + letter-spacing: -0.4px; + line-height: 40px; + margin: 0; + text-align: center; + vertical-align: middle; + + font-size: $font-title-medium; + + @include break-medium { + font-size: $font-title-large; + } + } + + .stepper-loader__progress-bar { + margin: 46px auto 0 auto; + } + + .stepper-loader__subtitle { + font-size: 1rem; + line-height: 21px; + letter-spacing: -0.02em; + color: var(--studio-gray-50); + margin-top: 24px; + } } diff --git a/client/landing/stepper/declarative-flow/internals/global.scss b/client/landing/stepper/declarative-flow/internals/global.scss index e0bd332ce53ac8..17b57b5bb22278 100644 --- a/client/landing/stepper/declarative-flow/internals/global.scss +++ b/client/landing/stepper/declarative-flow/internals/global.scss @@ -108,7 +108,8 @@ button { .hundred-year-domain, .link-in-bio-tld, .entrepreneur, -.generate-content { +.generate-content, +.processing { box-sizing: border-box; &.step-route { @@ -226,6 +227,10 @@ button { } +.step-route.onboarding { + margin: 0 auto; +} + .import-focused .step-container.site-picker, .import-hosted-site .step-container.site-picker { max-width: 1280px; @@ -401,17 +406,6 @@ button { } } -.wooexpress, -.copy-site, -.ecommerce { - .step-container.processing-step { - display: flex; - flex-direction: column; - align-items: center; - margin-top: 25vh; - } -} - .assign-trial-plan, .processing { &.wooexpress, @@ -449,13 +443,21 @@ button { } .wooexpress { + .step-container.processing-step { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 9vh; + } .step-container .step-container__content h1 { font-family: proxima-nova, sans-serif; font-weight: 600; font-size: $font-title-large; } - .loading-bar { + .stepper-loader__progress-bar { + --wp-components-color-foreground: var(--wooexpress-purple); background-color: var(--studio-woocommerce-purple-5); + height: 6px; width: 590px; max-width: 100%; margin-left: auto; @@ -470,7 +472,7 @@ button { max-width: 640px; box-sizing: border-box; } - p.processing-step__subtitle { + p.stepper-loader__subtitle { font-family: "SF Pro Text", $sans; font-weight: 400; letter-spacing: initial; diff --git a/client/landing/stepper/declarative-flow/internals/index.tsx b/client/landing/stepper/declarative-flow/internals/index.tsx index 9e9a8043cefe05..621c1c444d68e4 100644 --- a/client/landing/stepper/declarative-flow/internals/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/index.tsx @@ -6,6 +6,7 @@ import Modal from 'react-modal'; import { generatePath, useParams } from 'react-router'; import { Route, Routes } from 'react-router-dom'; import DocumentHead from 'calypso/components/data/document-head'; +import WordPressLogo from 'calypso/components/wordpress-logo'; import { STEPPER_INTERNAL_STORE } from 'calypso/landing/stepper/stores'; import AsyncCheckoutModal from 'calypso/my-sites/checkout/modal/async'; import { useSelector } from 'calypso/state'; @@ -127,6 +128,9 @@ export const FlowRenderer: React.FC< { flow: Flow; steps: readonly StepperStep[] const renderStep = ( step: StepperStep ) => { switch ( assertCondition.state ) { case AssertConditionState.CHECKING: + if ( isWooExpressFlow( flow.name ) ) { + return ; + } return ; case AssertConditionState.FAILURE: return null; @@ -201,7 +205,7 @@ export const FlowRenderer: React.FC< { flow: Flow; steps: readonly StepperStep[] useSignUpStartTracking( { flow } ); return ( - }> + }> diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/index.tsx index c1065037a96260..ff5d53dc12e0e6 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/index.tsx @@ -1,10 +1,9 @@ -import { isWooExpressFlow, StepContainer } from '@automattic/onboarding'; +import { StepContainer } from '@automattic/onboarding'; import { useSelect } from '@wordpress/data'; import { useI18n } from '@wordpress/react-i18n'; import { useEffect } from 'react'; import DocumentHead from 'calypso/components/data/document-head'; -import { LoadingBar } from 'calypso/components/loading-bar'; -import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import { ONBOARD_STORE } from 'calypso/landing/stepper/stores'; import { recordTracksEvent } from 'calypso/lib/analytics/tracks'; import wpcom from 'calypso/lib/wp'; @@ -14,7 +13,7 @@ import type { OnboardSelect } from '@automattic/data-stores'; import './styles.scss'; -const AssignTrialPlanStep: Step = function AssignTrialPlanStep( { navigation, data, flow } ) { +const AssignTrialPlanStep: Step = function AssignTrialPlanStep( { navigation, data } ) { const { submit } = navigation; const { __ } = useI18n(); const progress = useSelect( @@ -76,24 +75,13 @@ const AssignTrialPlanStep: Step = function AssignTrialPlanStep( { navigation, da shouldHideNavButtons hideFormattedHeader stepName="assign-trial-step" - isHorizontalLayout recordTracksEvent={ recordTracksEvent } stepContent={ - <> -
-

{ getCurrentMessage() }

- { progress >= 0 || isWooExpressFlow( flow ) ? ( - - ) : ( - - ) } - { isWooExpressFlow( flow ) ? ( -

{ getSubTitle() }

- ) : ( - <> - ) } -
- + } showFooterWooCommercePowered={ false } /> diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/styles.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/styles.scss index d9412b2a3ac432..0a52113e0aa83b 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/styles.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/assign-trial-plan/styles.scss @@ -12,14 +12,12 @@ padding: 1em; max-width: 540px; text-align: center; - margin: 16vh auto 0; } > .assign-trial-step { display: flex; flex-direction: column; align-items: center; - margin-top: 25vh; } .step-container { diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/automated-copy-site/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/automated-copy-site/index.tsx index f47d8442b2c83f..c8a922e4e9a769 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/automated-copy-site/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/automated-copy-site/index.tsx @@ -78,16 +78,16 @@ const AutomatedCopySite: Step = function AutomatedCopySite( { navigation } ) { switch ( transferStatus ) { case transferStates.PENDING: - setProgress( 0.2 ); + setProgress( 20 ); break; case transferStates.ACTIVE: - setProgress( 0.4 ); + setProgress( 40 ); break; case transferStates.PROVISIONED: - setProgress( 0.5 ); + setProgress( 50 ); break; case transferStates.COMPLETED: - setProgress( 0.7 ); + setProgress( 70 ); break; } @@ -98,7 +98,7 @@ const AutomatedCopySite: Step = function AutomatedCopySite( { navigation } ) { stopPollingTransfer = transferStatus === transferStates.COMPLETED; } - setProgress( 1 ); + setProgress( 100 ); return { finishedWaitingForCopy: true, siteSlug }; } ); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-install-plugins/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-install-plugins/index.tsx index 15dd78074f5cc2..5ff83b1bd21fa6 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-install-plugins/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-install-plugins/index.tsx @@ -102,7 +102,7 @@ const BundleInstallPlugins: Step = function BundleInstallPlugins( { navigation } let status: AtomicSoftwareStatus | undefined; let currentProgress = 0; const expectedStepCount = 5; - const progressStep = 1 / expectedStepCount; + const progressStep = 100 / expectedStepCount; while ( ! status?.applied ) { if ( maxFinishTime < new Date().getTime() ) { handleTransferFailure( { @@ -137,7 +137,7 @@ const BundleInstallPlugins: Step = function BundleInstallPlugins( { navigation } setProgress( currentProgress ); } - setProgress( 1 ); + setProgress( 100 ); } ); submit?.(); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-transfer/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-transfer/index.tsx index fc83bfc2eeb636..e3841ffb41777c 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-transfer/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/bundle-transfer/index.tsx @@ -137,16 +137,16 @@ const BundleTransfer: Step = function BundleTransfer( { navigation, flow } ) { switch ( transferStatus ) { case transferStates.PENDING: - setProgress( 0.2 ); + setProgress( 20 ); break; case transferStates.ACTIVE: - setProgress( 0.4 ); + setProgress( 40 ); break; case transferStates.PROVISIONED: - setProgress( 0.5 ); + setProgress( 50 ); break; case transferStates.COMPLETED: - setProgress( 0.7 ); + setProgress( 70 ); break; } @@ -208,7 +208,7 @@ const BundleTransfer: Step = function BundleTransfer( { navigation, flow } ) { await wait( 3000 ); } } - setProgress( 1 ); + setProgress( 100 ); } ); submit?.(); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/index.tsx index 54edb281fa4e38..f576635aa5dbcf 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/index.tsx @@ -30,11 +30,10 @@ import { useI18n } from '@wordpress/react-i18n'; import { getQueryArg } from '@wordpress/url'; import { useEffect } from 'react'; import DocumentHead from 'calypso/components/data/document-head'; -import { LoadingBar } from 'calypso/components/loading-bar'; -import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; import useAddEcommerceTrialMutation from 'calypso/data/ecommerce/use-add-ecommerce-trial-mutation'; import useAddTempSiteToSourceOptionMutation from 'calypso/data/site-migration/use-add-temp-site-mutation'; import { useSourceMigrationStatusQuery } from 'calypso/data/site-migration/use-source-migration-status-query'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import { useQuery } from 'calypso/landing/stepper/hooks/use-query'; import { ONBOARD_STORE } from 'calypso/landing/stepper/stores'; import { recordTracksEvent } from 'calypso/lib/analytics/tracks'; @@ -312,18 +311,13 @@ const CreateSite: Step = function CreateSite( { navigation, flow, data } ) { shouldHideNavButtons hideFormattedHeader stepName="create-site" - isHorizontalLayout recordTracksEvent={ recordTracksEvent } stepContent={ - <> -

{ getCurrentMessage() }

- { progress >= 0 || isWooExpressFlow( flow ) ? ( - - ) : ( - - ) } - { subTitle &&

{ subTitle }

} - + } showFooterWooCommercePowered={ false } /> diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/styles.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/styles.scss index 7b6a8be559c016..8a6e5e3007b1c2 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/styles.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/create-site/styles.scss @@ -7,7 +7,6 @@ $font-family: "SF Pro Text", $sans; padding: 1em; max-width: 540px; text-align: center; - margin: 16vh auto 0; .step-container__content { margin: auto; diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx index 95d334c6525fe3..124c5e7030b1dc 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/design-setup/unified-design-picker.tsx @@ -22,11 +22,16 @@ import { PERSONAL_THEME, } from '@automattic/design-picker'; import { useLocale, useHasEnTranslation } from '@automattic/i18n-utils'; -import { StepContainer, DESIGN_FIRST_FLOW, ONBOARDING_FLOW } from '@automattic/onboarding'; +import { + StepContainer, + DESIGN_FIRST_FLOW, + ONBOARDING_FLOW, + isSiteSetupFlow, +} from '@automattic/onboarding'; import { useSelect, useDispatch } from '@wordpress/data'; import { addQueryArgs } from '@wordpress/url'; import { useTranslate } from 'i18n-calypso'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import AsyncLoad from 'calypso/components/async-load'; import QueryEligibility from 'calypso/components/data/query-atat-eligibility'; import { useQueryProductsList } from 'calypso/components/data/query-products-list'; @@ -91,7 +96,6 @@ import useRecipe from './hooks/use-recipe'; import useTrackFilters from './hooks/use-track-filters'; import getThemeIdFromDesign from './utils/get-theme-id-from-design'; import type { Step, ProvidedDependencies } from '../../types'; -import './style.scss'; import type { OnboardSelect, SiteSelect, @@ -102,6 +106,7 @@ import type { Design, StyleVariation } from '@automattic/design-picker'; import type { GlobalStylesObject } from '@automattic/global-styles'; import type { AnyAction } from 'redux'; import type { ThunkAction } from 'redux-thunk'; +import './style.scss'; const SiteIntent = Onboard.SiteIntent; @@ -171,6 +176,9 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { ( select ) => site && ( select( SITE_STORE ) as SiteSelect ).isSiteAtomic( site.ID ), [ site ] ); + const { setPendingAction } = useDispatch( ONBOARD_STORE ); + const isComingFromTheUpgradeScreen = queryParams.get( 'continue' ) === '1'; + useEffect( () => { if ( isAtomic ) { // TODO: move this logic from this step to the flow(s). See: https://wp.me/pdDR7T-KR @@ -191,6 +199,8 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { ) ); + const { setDesignOnSite, assembleSite } = useDispatch( SITE_STORE ); + // ********** Logic for fetching designs const selectStarterDesigns = ( allDesigns: StarterDesigns ) => { if ( disableCheckoutImmediately ) { @@ -501,7 +511,9 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { ? addQueryArgs( `/marketplace/thank-you/${ wpcomSiteSlug ?? siteSlug }?onboarding`, { themes: selectedDesign?.slug, } ) - : window.location.href.replace( window.location.origin, '' ); + : addQueryArgs( window.location.href.replace( window.location.origin, '' ), { + continue: 1, + } ); goToCheckout( { flowName: flow, @@ -597,6 +609,117 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { } } + const handleSubmit = useCallback( + ( providedDependencies?: ProvidedDependencies, optionalProps?: object ) => { + const _selectedDesign = providedDependencies?.selectedDesign as Design; + recordSelectedDesign( { + ...commonFilterProperties, + flow, + intent, + design: _selectedDesign, + styleVariation: selectedStyleVariation, + colorVariation: selectedColorVariation, + fontVariation: selectedFontVariation, + optionalProps, + } ); + + submit?.( { + ...providedDependencies, + eventProps: commonFilterProperties, + } ); + }, + [ + commonFilterProperties, + flow, + intent, + selectedStyleVariation, + selectedColorVariation, + selectedFontVariation, + submit, + ] + ); + + const pickDesign = useCallback( + async ( _selectedDesign: Design | undefined = selectedDesign ) => { + setSelectedDesign( _selectedDesign ); + + if ( siteSlugOrId ) { + await updateLaunchpadSettings( siteSlugOrId, { + checklist_statuses: { design_completed: true }, + } ); + } + + if ( siteSlugOrId && _selectedDesign ) { + const positionIndex = designs.findIndex( + ( design ) => design.slug === _selectedDesign?.slug + ); + + setPendingAction( () => { + if ( _selectedDesign?.is_virtual ) { + const themeId = getThemeIdFromStylesheet( _selectedDesign.recipe?.stylesheet ?? '' ); + return Promise.resolve() + .then( () => + reduxDispatch( + activateOrInstallThenActivate( themeId ?? '', site?.ID ?? 0, { + source: 'assembler', + } ) as ThunkAction< PromiseLike< string >, any, any, AnyAction > + ) + ) + .then( ( activeThemeStylesheet: string ) => + assembleSite( siteSlugOrId, activeThemeStylesheet, { + homeHtml: _selectedDesign.recipe?.pattern_html, + headerHtml: _selectedDesign.recipe?.header_html, + footerHtml: _selectedDesign.recipe?.footer_html, + siteSetupOption: 'assembler-virtual-theme', + } ) + ); + } + + return setDesignOnSite( siteSlugOrId, _selectedDesign, { + styleVariation: selectedStyleVariation, + globalStyles, + } ).then( ( theme: ActiveTheme ) => { + return reduxDispatch( setActiveTheme( site?.ID || -1, theme ) ); + } ); + } ); + + handleSubmit( + { + selectedDesign: _selectedDesign, + }, + { ...( positionIndex >= 0 && { position_index: positionIndex } ) } + ); + } else if ( ! isSiteRequired && ! siteSlugOrId && _selectedDesign ) { + const positionIndex = designs.findIndex( + ( design ) => design.slug === _selectedDesign?.slug + ); + handleSubmit( + { + selectedDesign: _selectedDesign, + selectedSiteCategory: categorization.selections?.join( ',' ), + }, + { ...( positionIndex >= 0 && { position_index: positionIndex } ) } + ); + } + }, + [ + assembleSite, + categorization.selections, + designs, + globalStyles, + handleSubmit, + isSiteRequired, + reduxDispatch, + selectedDesign, + selectedStyleVariation, + setDesignOnSite, + setPendingAction, + setSelectedDesign, + site?.ID, + siteSlugOrId, + ] + ); + function tryPremiumGlobalStyles() { // These conditions should be true at this point, but just in case... if ( shouldUnlockGlobalStyles ) { @@ -613,98 +736,11 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { } } - // ********** Logic for submitting the selected design - - const { setDesignOnSite, assembleSite } = useDispatch( SITE_STORE ); - const { setPendingAction } = useDispatch( ONBOARD_STORE ); - - async function pickDesign( _selectedDesign: Design | undefined = selectedDesign ) { - setSelectedDesign( _selectedDesign ); - - if ( siteSlugOrId ) { - await updateLaunchpadSettings( siteSlugOrId, { - checklist_statuses: { design_completed: true }, - } ); - } - - if ( siteSlugOrId && _selectedDesign ) { - const positionIndex = designs.findIndex( - ( design ) => design.slug === _selectedDesign?.slug - ); - - setPendingAction( () => { - if ( _selectedDesign?.is_virtual ) { - const themeId = getThemeIdFromStylesheet( _selectedDesign.recipe?.stylesheet ?? '' ); - return Promise.resolve() - .then( () => - reduxDispatch( - activateOrInstallThenActivate( themeId ?? '', site?.ID ?? 0, { - source: 'assembler', - } ) as ThunkAction< PromiseLike< string >, any, any, AnyAction > - ) - ) - .then( ( activeThemeStylesheet: string ) => - assembleSite( siteSlugOrId, activeThemeStylesheet, { - homeHtml: _selectedDesign.recipe?.pattern_html, - headerHtml: _selectedDesign.recipe?.header_html, - footerHtml: _selectedDesign.recipe?.footer_html, - siteSetupOption: 'assembler-virtual-theme', - } ) - ); - } - - return setDesignOnSite( siteSlugOrId, _selectedDesign, { - styleVariation: selectedStyleVariation, - globalStyles, - } ).then( ( theme: ActiveTheme ) => { - return reduxDispatch( setActiveTheme( site?.ID || -1, theme ) ); - } ); - } ); - - handleSubmit( - { - selectedDesign: _selectedDesign, - }, - { ...( positionIndex >= 0 && { position_index: positionIndex } ) } - ); - } else if ( ! isSiteRequired && ! siteSlugOrId && _selectedDesign ) { - const positionIndex = designs.findIndex( - ( design ) => design.slug === _selectedDesign?.slug - ); - handleSubmit( - { - selectedDesign: _selectedDesign, - selectedSiteCategory: categorization.selections?.join( ',' ), - }, - { ...( positionIndex >= 0 && { position_index: positionIndex } ) } - ); - } - } - function pickUnlistedDesign( theme: string ) { // TODO: move this logic from this step to the flow(s). See: https://wp.me/pdDR7T-KR exitFlow?.( `/theme/${ theme }/${ siteSlug }` ); } - function handleSubmit( providedDependencies?: ProvidedDependencies, optionalProps?: object ) { - const _selectedDesign = providedDependencies?.selectedDesign as Design; - recordSelectedDesign( { - ...commonFilterProperties, - flow, - intent, - design: _selectedDesign, - styleVariation: selectedStyleVariation, - colorVariation: selectedColorVariation, - fontVariation: selectedFontVariation, - optionalProps, - } ); - - submit?.( { - ...providedDependencies, - eventProps: commonFilterProperties, - } ); - } - function handleBackClick() { if ( isPreviewingDesign ) { recordTracksEvent( @@ -771,10 +807,20 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { ); } + useEffect( () => { + if ( isComingFromTheUpgradeScreen ) { + pickDesign(); + } + }, [ isComingFromTheUpgradeScreen, pickDesign ] ); + // ********** Main render logic // Don't render until we've done fetching all the data needed for initial render. - if ( ( ! site && isSiteRequired ) || isLoadingDesigns || isGoalsAtFrontExperimentLoading ) { + const isSiteLoading = ! site && isSiteRequired; + const isDesignsLoading = isLoadingDesigns || isGoalsAtFrontExperimentLoading; + const isLoading = isSiteLoading || isDesignsLoading; + + if ( isLoading || isComingFromTheUpgradeScreen ) { return ; } @@ -804,6 +850,7 @@ const UnifiedDesignPickerStep: Step = ( { navigation, flow, stepName } ) => { { getOptionsMenu={ isUpdatedBadgeDesign ? getBadge : undefined } oldHighResImageLoading={ oldHighResImageLoading } siteActiveTheme={ siteActiveTheme?.[ 0 ]?.stylesheet ?? null } - showActiveThemeBadge={ intent !== 'build' } + showActiveThemeBadge={ intent !== SiteIntent.Build && ! isSiteSetupFlow( flow ) } isMultiFilterEnabled isBigSkyEligible={ isBigSkyEligible } /> diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/domains/style.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/domains/style.scss index 5c56797838a48a..a4a6822046782e 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/domains/style.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/domains/style.scss @@ -602,11 +602,6 @@ .domain-search-results__domain-available-notice-icon { margin-right: 4px; } - - /* hide transfer link */ - a { - display: none; - } } // Hide the domain explanation image @@ -629,12 +624,6 @@ margin-right: 16px; } } - - .domain-search-results__domain-available-notice { - a { - display: unset; - } - } } } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/hundred-year-plan-setup/styles.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/hundred-year-plan-setup/styles.scss index e08688917db523..d35112cd13f99b 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/hundred-year-plan-setup/styles.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/hundred-year-plan-setup/styles.scss @@ -23,5 +23,8 @@ } margin: 32px 16px; } + .setup-form__form{ + margin: 0 auto; + } } } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/launch-big-sky/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/launch-big-sky/index.tsx index 13a039c037dbde..345c209a4b86cf 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/launch-big-sky/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/launch-big-sky/index.tsx @@ -1,10 +1,10 @@ import { Onboard } from '@automattic/data-stores'; import { getAssemblerDesign } from '@automattic/design-picker'; -import { ProgressBar } from '@wordpress/components'; import { resolveSelect, useDispatch, useSelect } from '@wordpress/data'; import { useI18n } from '@wordpress/react-i18n'; import { useEffect, FormEvent, useState } from 'react'; import wpcomRequest from 'wpcom-proxy-request'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import { SITE_STORE, ONBOARD_STORE } from 'calypso/landing/stepper/stores'; import { useIsBigSkyEligible } from '../../../../hooks/use-is-site-big-sky-eligible'; import { useSiteData } from '../../../../hooks/use-site-data'; @@ -122,19 +122,14 @@ const LaunchBigSky: Step = function () { function LaunchingBigSky() { return ( -
-
-

- { __( 'Launching the AI Website Builder' ) } -

- { ! isError && } - { isError && ( -

- { __( 'Something unexpected happened. Please go back and try again.' ) } -

- ) } -
-
+ <> + { ! isError && } + { isError && ( +

+ { __( 'Something unexpected happened. Please go back and try again.' ) } +

+ ) } + ); } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/style.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/style.scss index 4b2f949a4da42b..e44b0406159250 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/style.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/launchpad/style.scss @@ -300,7 +300,7 @@ // Launchpad - Processing Screens .processing { - .processing-step__progress-bar { + .stepper-loader__progress-bar { background-color: #fff; } @@ -340,8 +340,10 @@ .progress-bar { display: none; } +} - h1.processing-step__progress-step { +.stepper-loader { + h1.stepper-loader__title { font-size: $font-title-medium; @include break-medium { diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/README.md b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/README.md index f3dbfc3a4873f0..c83d22fad302dd 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/README.md +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/README.md @@ -59,13 +59,13 @@ setPendingAction( async () => { setProgress( 0 ); await goToServerAndWork(); - setProgress( 0.35 ); + setProgress( 35 ); await doMoreWork(); - setProgress( 0.8 ); + setProgress( 80 ); await doEvenMoreWork(); - setProgress( 1 ); + setProgress( 100 ); }); submit?.(); @@ -105,15 +105,15 @@ setPendingAction( async () => { setProgressTitle( 'Working...' ); await goToServerAndWork(); - setProgress( 0.35 ); + setProgress( 35 ); setProgressTitle( 'Working harder!' ); await doMoreWork(); - setProgress( 0.8 ); + setProgress( 80 ); setProgressTitle( 'Almost done' ); await doEvenMoreWork(); - setProgress( 1 ); + setProgress( 100 ); }); submit?.(); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx index 1dac0a370d9852..0849bd1c3ebdab 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx @@ -5,19 +5,16 @@ import { isNewSiteMigrationFlow, isUpdateDesignFlow, ECOMMERCE_FLOW, - isWooExpressFlow, - isTransferringHostedSiteCreationFlow, HUNDRED_YEAR_DOMAIN_FLOW, HUNDRED_YEAR_PLAN_FLOW, HUNDRED_YEAR_DOMAIN_TRANSFER, isAnyHostingFlow, } from '@automattic/onboarding'; -import { ProgressBar } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useI18n } from '@wordpress/react-i18n'; import { useEffect, useState, useRef } from 'react'; import DocumentHead from 'calypso/components/data/document-head'; -import { LoadingBar } from 'calypso/components/loading-bar'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import availableFlows from 'calypso/landing/stepper/declarative-flow/registered-flows'; import { useRecordSignupComplete } from 'calypso/landing/stepper/hooks/use-record-signup-complete'; import { ONBOARD_STORE } from 'calypso/landing/stepper/stores'; @@ -33,6 +30,7 @@ import TailoredFlowPreCheckoutScreen from './tailored-flow-precheckout-screen'; import type { StepProps } from '../../types'; import type { OnboardSelect } from '@automattic/data-stores'; import './style.scss'; + interface ProcessingStepProps extends StepProps { title?: string; subtitle?: string; @@ -195,26 +193,6 @@ const ProcessingStep: React.FC< ProcessingStepProps > = function ( props ) { return ; } - const subtitle = getSubtitle(); - - const renderProgressComponent = () => { - if ( isWooExpressFlow( flow ) || isTransferringHostedSiteCreationFlow( flow ) ) { - return ( - - ); - } - - return ( - = 0 ? progress * 100 : undefined } - className="processing-step__progress-bar" - /> - ); - }; - return ( <> @@ -223,13 +201,11 @@ const ProcessingStep: React.FC< ProcessingStepProps > = function ( props ) { hideFormattedHeader stepName="processing-step" stepContent={ - <> -
-

{ getCurrentMessage() }

- { renderProgressComponent() } - { subtitle &&

{ subtitle }

} -
- + } recordTracksEvent={ recordTracksEvent } showJetpackPowered={ isJetpackPowered } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/style.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/style.scss index ee49502aa515f3..a8c6282f7995f3 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/style.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/style.scss @@ -4,11 +4,8 @@ .processing-step { --wp-components-color-foreground: #3858E9; - - padding: 1em; max-width: 540px; text-align: center; - margin: 16vh auto 0; &__progress-step { @include onboarding-font-recoleta; @@ -19,10 +16,6 @@ vertical-align: middle; margin: 0; } - - .processing-step__progress-bar { - margin: 46px auto 0 auto; - } } .processing-step__step-wrapper { @@ -39,27 +32,19 @@ width: 100%; } -.processing-step__subtitle { - font-size: 1rem; - line-height: 21px; - letter-spacing: -0.02em; - color: var(--studio-gray-50); - margin-top: 24px; -} - // Processing Copy Site Styles .copy-site.processing-copy { - .processing-step { + .processing-step, .stepper-loader { margin: 5vh auto 0; max-width: 660px; } - .processing-step__progress-step { + .stepper-loader__title { font-size: 3rem; margin-bottom: 24px; } - .processing-step__progress-bar { + .stepper-loader__progress-bar { max-width: 540px; margin: 0 auto; } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/index.tsx index 5b07408a2822fa..225ada51307325 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/index.tsx @@ -1,14 +1,9 @@ -import { - NEWSLETTER_FLOW, - LINK_IN_BIO_TLD_FLOW, - isNewsletterOrLinkInBioFlow, -} from '@automattic/onboarding'; +import { NEWSLETTER_FLOW, LINK_IN_BIO_TLD_FLOW } from '@automattic/onboarding'; import { useI18n } from '@wordpress/react-i18n'; import PropTypes from 'prop-types'; import { useRef, useState, useEffect } from 'react'; import JetpackLogo from 'calypso/components/jetpack-logo'; -import { LoadingBar } from 'calypso/components/loading-bar'; -import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import { useInterval } from 'calypso/lib/interval/use-interval'; import './style.scss'; @@ -62,11 +57,12 @@ export default function TailoredFlowPreCheckoutScreen( { flowName }: { flowName: const defaultDuration = DURATION_IN_MS / totalSteps; const duration = steps.current[ currentStep ]?.duration || defaultDuration; - /** - * Completion progress: 0 <= progress <= 1 - */ - const progress = ( currentStep + 1 ) / totalSteps; - const isComplete = progress >= 1; + // Force animated progress bar to start at 0 + const [ hasStarted, setHasStarted ] = useState( false ); + useEffect( () => { + const id = setTimeout( () => setHasStarted( true ), 750 ); + return () => clearTimeout( id ); + }, [] ); // Temporarily override document styles to prevent scrollbars from showing useEffect( () => { @@ -75,6 +71,13 @@ export default function TailoredFlowPreCheckoutScreen( { flowName }: { flowName: document.documentElement.classList.remove( 'no-scroll' ); }; }, [] ); + /** + * Completion progress: 0 <= progress <= 100 + */ + const progress = ! hasStarted + ? /* initial 10% progress */ 10 + : ( ( currentStep + 1 ) * 100 ) / totalSteps; + const isComplete = progress >= 100; useInterval( () => setCurrentStep( ( s ) => s + 1 ), @@ -82,33 +85,15 @@ export default function TailoredFlowPreCheckoutScreen( { flowName }: { flowName: isComplete ? null : duration ); - // Force animated progress bar to start at 0 - const [ hasStarted, setHasStarted ] = useState( false ); - useEffect( () => { - const id = setTimeout( () => setHasStarted( true ), 750 ); - return () => clearTimeout( id ); - }, [] ); - return ( -
-
-

{ steps.current[ currentStep ]?.title }

- { isNewsletterOrLinkInBioFlow( flowName ) ? ( - - ) : ( - - ) } -
- + <> + { flowName === NEWSLETTER_FLOW && (
Jetpack powered
) } -
+ ); } diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/style.scss b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/style.scss index 7ea27616c8d373..f29592debc7063 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/style.scss +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/tailored-flow-precheckout-screen/style.scss @@ -18,10 +18,6 @@ } } - .processing-step__progress-bar { - background-color: var(--studio-white); - } - .processing-step__container { height: 100%; width: 100%; @@ -47,14 +43,6 @@ } } - h1.processing-step__progress-step { - font-size: $font-title-medium; - - @include break-medium { - font-size: $font-title-large; - } - } - .processing-step__jetpack-powered { display: flex; justify-content: center; @@ -74,7 +62,7 @@ background-color: var(--studio-gray-0); } - .loading-bar { + .stepper-loader__progress-bar { background-color: var(--studio-gray-5); &::before { background: var(--studio-black); diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/site-launch/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/site-launch/index.tsx index 71d4ab22bc24d5..6fbc3e791a2dd2 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/site-launch/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/site-launch/index.tsx @@ -1,9 +1,9 @@ import { StepContainer } from '@automattic/onboarding'; import { useDispatch } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; import { useI18n } from '@wordpress/react-i18n'; -import { useEffect } from 'react'; import DocumentHead from 'calypso/components/data/document-head'; -import { LoadingEllipsis } from 'calypso/components/loading-ellipsis'; +import { StepperLoader } from 'calypso/landing/stepper/declarative-flow/internals/components'; import { useSite } from 'calypso/landing/stepper/hooks/use-site'; import { ONBOARD_STORE, SITE_STORE } from 'calypso/landing/stepper/stores'; import { recordTracksEvent } from 'calypso/lib/analytics/tracks'; @@ -45,14 +45,7 @@ const SiteLaunchStep: React.FC< SiteLaunchStepProps > = function ( props ) { shouldHideNavButtons hideFormattedHeader stepName="processing-step" - stepContent={ - <> -
-

{ __( 'Launching blog' ) }

- -
- - } + stepContent={ } recordTracksEvent={ recordTracksEvent } /> diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/index.tsx index 0763c115c8c3cc..967fbc6faaf289 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/index.tsx @@ -73,7 +73,7 @@ const SiteMigrationPluginInstall: Step = ( { navigation } ) => { }, } ); - setProgress( 1 ); + setProgress( 100 ); return { pluginInstalled: true, diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/test/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/test/index.tsx index 6b9e1054fbcca3..fe30144a4cdaf6 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/test/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/site-migration-plugin-install/test/index.tsx @@ -59,7 +59,7 @@ describe( 'SiteMigrationPluginInstall', () => { expect( result.pluginInstalled ).toBe( true ); expect( nock.isDone() ).toBe( true ); - expect( getProgress() ).toBe( 1 ); + expect( getProgress() ).toBe( 100 ); } ); it( 'installs and activates the plugin when it is not installed', async () => { @@ -85,7 +85,7 @@ describe( 'SiteMigrationPluginInstall', () => { expect( result.pluginInstalled ).toBe( true ); expect( nock.isDone() ).toBe( true ); - expect( getProgress() ).toBe( 1 ); + expect( getProgress() ).toBe( 100 ); } ); it( 'polls the plugin endpoint until we have information about the plugins', async () => { @@ -109,6 +109,6 @@ describe( 'SiteMigrationPluginInstall', () => { expect( result.pluginInstalled ).toBe( true ); expect( nock.isDone() ).toBe( true ); - expect( getProgress() ).toBe( 1 ); + expect( getProgress() ).toBe( 100 ); } ); } ); diff --git a/client/me/concierge/shared/closure-notice.js b/client/me/concierge/shared/closure-notice.js index 49b09fbe65962c..8e1d3d05647d17 100644 --- a/client/me/concierge/shared/closure-notice.js +++ b/client/me/concierge/shared/closure-notice.js @@ -1,11 +1,12 @@ import 'moment-timezone'; // monkey patches the existing moment.js import { CompactCard as Card } from '@automattic/components'; +import { __experimentalVStack as VStack } from '@wordpress/components'; import { useTranslate } from 'i18n-calypso'; import { useLocalizedMoment } from 'calypso/components/localized-moment'; const DATE_FORMAT = 'LLL'; -const ClosureNotice = ( { closesAt, displayAt, reopensAt } ) => { +const ClosureNotice = ( { closesAt, displayAt, reopensAt, isGM } ) => { const translate = useTranslate(); const moment = useLocalizedMoment(); @@ -16,43 +17,86 @@ const ClosureNotice = ( { closesAt, displayAt, reopensAt } ) => { return null; } - let message; - - if ( currentDate.isBefore( closesAt ) ) { - message = translate( - '{{strong}}Notice:{{/strong}} Quick Start sessions will be closed from %(closesAt)s until %(reopensAt)s. ' + - 'If you need to get in touch with us, you’ll be able to {{link}}submit a support request{{/link}} ' + - 'and we’ll get to it as fast as we can. Thank you!', - { - args: { - closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), - reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), - }, - components: { - link: , - strong: , - }, - } - ); - } else { - message = translate( - '{{strong}}Quick Start Sessions will be closed from %(closesAt)s – %(reopensAt)s for the New Year’s holiday.{{/strong}}{{br/}}' + - 'If you need to get in touch with us, please submit a {{link}}support request from this page{{/link}} and we will get to it as fast as we can. ' + - 'Quick Start Sessions will re-open at %(reopensAt)s. Thank you for your understanding!', - { - args: { - closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), - reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), - }, - components: { - link: , - strong: , - br:
, - }, - } - ); - } - return { message }; + /** @type {Record} */ + const MESSAGES = { + default: { + before: translate( + '{{strong}}Notice:{{/strong}} Quick Start sessions will be closed from %(closesAt)s until %(reopensAt)s. ' + + 'If you need to get in touch with us, you’ll be able to {{link}}submit a support request{{/link}} ' + + 'and we’ll get to it as fast as we can. Thank you!', + { + args: { + closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), + reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), + }, + components: { + link:
, + strong: , + }, + } + ), + during: translate( + '{{strong}}Quick Start Sessions will be closed from %(closesAt)s – %(reopensAt)s for the New Year’s holiday.{{/strong}}{{br/}}' + + 'If you need to get in touch with us, please submit a {{link}}support request from this page{{/link}} and we will get to it as fast as we can. ' + + 'Quick Start Sessions will re-open at %(reopensAt)s. Thank you for your understanding!', + { + args: { + closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), + reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), + }, + components: { + link: , + strong: , + br:
, + }, + } + ), + }, + gm: { + before: translate( + '{{strong}}Note:{{/strong}} Support sessions will not be available between %(closesAt)s and %(reopensAt)s.', + { + args: { + closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), + reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), + }, + components: { + strong: , + }, + } + ), + during: translate( + '{{strong}}Note:{{/strong}} Support sessions are not available before %(reopensAt)s.', + { + args: { + reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), + }, + components: { + strong: , + }, + } + ), + reason: translate( + 'Why? Once a year, the WordPress.com Happiness Engineers and the rest of the WordPress.com family get together to work on improving our services, building new features, and learning how to better serve our customers like you. But never fear! If you need help in the meantime, you can submit an email ticket through the contact form: {{contactLink}}https://wordpress.com/help/contact{{/contactLink}}', + { + components: { + contactLink:
, + }, + } + ), + }, + }; + + const variant = MESSAGES[ isGM ? 'gm' : 'default' ]; + + return ( + + +
{ currentDate.isBefore( closesAt ) ? variant.before : variant.during }
+ { variant.reason &&
{ variant.reason }
} +
+
+ ); }; export default ClosureNotice; diff --git a/client/me/concierge/shared/gm-closure-notice.js b/client/me/concierge/shared/gm-closure-notice.js deleted file mode 100644 index c901bbaf1ec4f7..00000000000000 --- a/client/me/concierge/shared/gm-closure-notice.js +++ /dev/null @@ -1,65 +0,0 @@ -import 'moment-timezone'; // monkey patches the existing moment.js -import { CompactCard as Card } from '@automattic/components'; -import { useTranslate } from 'i18n-calypso'; -import { useLocalizedMoment } from 'calypso/components/localized-moment'; - -const DATE_FORMAT = 'dddd, MMMM Do LT'; - -const GMClosureNotice = ( { closesAt, displayAt, reopensAt } ) => { - const translate = useTranslate(); - const moment = useLocalizedMoment(); - - const currentDate = moment(); - const guessedTimezone = moment.tz.guess(); - - if ( ! currentDate.isBetween( displayAt, reopensAt ) ) { - return null; - } - - let message; - - if ( currentDate.isBefore( closesAt ) ) { - message = translate( - '{{strong}}Note:{{/strong}} Support sessions will not be available between %(closesAt)s and %(reopensAt)s.', - { - args: { - closesAt: moment.tz( closesAt, guessedTimezone ).format( DATE_FORMAT ), - reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), - }, - components: { - strong: , - }, - } - ); - } else { - message = translate( - '{{strong}}Note:{{/strong}} Support sessions are not available before %(reopensAt)s.', - { - args: { - reopensAt: moment.tz( reopensAt, guessedTimezone ).format( DATE_FORMAT ), - }, - components: { - strong: , - }, - } - ); - } - - const reason = translate( - 'Why? Once a year, the WordPress.com Happiness Engineers and the rest of the WordPress.com family get together to work on improving our services, building new features, and learning how to better serve our customers like you. But never fear! If you need help in the meantime, you can submit an email ticket through the contact form: {{contactLink}}https://wordpress.com/help/contact{{/contactLink}}', - { - components: { - contactLink:
, - }, - } - ); - - return ( - -

{ message }

-

{ reason }

-
- ); -}; - -export default GMClosureNotice; diff --git a/client/me/concierge/shared/primary-header.js b/client/me/concierge/shared/primary-header.js index 021d5ed7a04d69..c1ffc83aa6fd2b 100644 --- a/client/me/concierge/shared/primary-header.js +++ b/client/me/concierge/shared/primary-header.js @@ -16,6 +16,7 @@ class PrimaryHeader extends Component { displayAt="2023-12-26 00:00Z" closesAt="2023-12-31 00:00Z" reopensAt="2024-01-02 07:00Z" + isGM={ false } /> span { @@ -35,4 +37,4 @@ color: var( --studio-gray-60 ); } } -} \ No newline at end of file +} diff --git a/client/my-sites/checkout/src/components/item-variation-picker/variant-dropdown-price.tsx b/client/my-sites/checkout/src/components/item-variation-picker/variant-dropdown-price.tsx index a6ada6482db61a..2c6bc9e4dbf5a6 100644 --- a/client/my-sites/checkout/src/components/item-variation-picker/variant-dropdown-price.tsx +++ b/client/my-sites/checkout/src/components/item-variation-picker/variant-dropdown-price.tsx @@ -121,6 +121,11 @@ export const ItemVariantDropDownPrice: FunctionComponent< { { args } ); // translation example: $1 first month then $2 per year + } else if ( productBillingTermInMonths === 1 && introTerm === 'year' ) { + return translate( + '%(formattedCurrentPrice)s first year then %(formattedPriceBeforeDiscounts)s per month', + { args } + ); } return translate( '%(formattedCurrentPrice)s first month then %(formattedPriceBeforeDiscounts)s per month', @@ -146,8 +151,18 @@ export const ItemVariantDropDownPrice: FunctionComponent< { ); // translation example: $1 first 3 months then $2 per 2 years } else if ( productBillingTermInMonths === 12 ) { + return introTerm === 'month' + ? translate( + '%(formattedCurrentPrice)s first %(introCount)s months then %(formattedPriceBeforeDiscounts)s per year', + { args } + ) + : translate( + '%(formattedCurrentPrice)s first %(introCount)s years then %(formattedPriceBeforeDiscounts)s per year', + { args } + ); + } else if ( productBillingTermInMonths === 1 && introTerm === 'year' ) { return translate( - '%(formattedCurrentPrice)s first %(introCount)s months then %(formattedPriceBeforeDiscounts)s per year', + '%(formattedCurrentPrice)s first %(introCount)s years then %(formattedPriceBeforeDiscounts)s per month', { args } ); // translation example: $1 first 3 months then $2 per year diff --git a/client/my-sites/domains/domain-management/components/domain-header/index.tsx b/client/my-sites/domains/domain-management/components/domain-header/index.tsx index 530aac49f33a26..02611863df9c4d 100644 --- a/client/my-sites/domains/domain-management/components/domain-header/index.tsx +++ b/client/my-sites/domains/domain-management/components/domain-header/index.tsx @@ -49,6 +49,7 @@ const DomainHeader = ( { mobileItem={ mobileItem } title={ titleOverride || items[ items.length - 1 ].label } subtitle={ subtitleOverride || items[ items.length - 1 ].subtitle } + className="domain-header" > { renderButtons() } diff --git a/client/my-sites/domains/domain-management/components/domain-header/style.scss b/client/my-sites/domains/domain-management/components/domain-header/style.scss index 1257060fc4d11c..5b03f0a2eee3b5 100644 --- a/client/my-sites/domains/domain-management/components/domain-header/style.scss +++ b/client/my-sites/domains/domain-management/components/domain-header/style.scss @@ -1,6 +1,17 @@ @import "@wordpress/base-styles/breakpoints"; @import "@wordpress/base-styles/mixins"; +.domain-header { + .navigation-header__main { + align-items: center; + + .formatted-header__title { + font-size: 1.5rem; + line-height: 1.25; + } + } +} + .domain-header__header-spacer { height: 58px; diff --git a/client/my-sites/domains/domain-management/domain-overview-pane/style.scss b/client/my-sites/domains/domain-management/domain-overview-pane/style.scss index 8dfee19851ea97..9c62a2b48c67ff 100644 --- a/client/my-sites/domains/domain-management/domain-overview-pane/style.scss +++ b/client/my-sites/domains/domain-management/domain-overview-pane/style.scss @@ -208,6 +208,17 @@ flex-wrap: wrap; } + .domains-overview__details { + .main.is-wide-layout:not( .email-providers-in-depth-comparison-page ):not( .email-stacked-comparison-page ) { + margin-left: 0; + max-width: 1040px; + } + + .hosting-dashboard-layout-column__container .navigation-header div.navigation-header__main { + margin-left: 0; + } + } + .hosting-dashboard-item-view__content > div { flex-grow: 1; diff --git a/client/my-sites/email/add-mailboxes/index.tsx b/client/my-sites/email/add-mailboxes/index.tsx index 0215c9035981a5..ea8cd5a54c5ef2 100644 --- a/client/my-sites/email/add-mailboxes/index.tsx +++ b/client/my-sites/email/add-mailboxes/index.tsx @@ -18,12 +18,6 @@ import { GOOGLE_PROVIDER_NAME } from 'calypso/lib/gsuite/constants'; import { getTitanProductName } from 'calypso/lib/titan'; import { TITAN_PROVIDER_NAME } from 'calypso/lib/titan/constants'; import useCartKey from 'calypso/my-sites/checkout/use-cart-key'; -import { - domainManagementAllEmailRoot, - domainSiteContextRoot, - isUnderDomainManagementAll, - isUnderDomainSiteContext, -} from 'calypso/my-sites/domains/paths'; import AddEmailAddressesCardPlaceholder from 'calypso/my-sites/email/add-mailboxes/add-users-placeholder'; import EmailProviderPricingNotice from 'calypso/my-sites/email/add-mailboxes/email-provider-pricing-notice'; import { @@ -47,6 +41,7 @@ import { EmailProvider } from 'calypso/my-sites/email/form/mailboxes/types'; import { usePasswordResetEmailField } from 'calypso/my-sites/email/hooks/use-password-reset-email-field'; import { MAILBOXES_SOURCE } from 'calypso/my-sites/email/mailboxes/constants'; import { + getEmailCheckoutPath, getEmailManagementPath, getMailboxesPath, getTitanSetUpMailboxPath, @@ -325,17 +320,12 @@ const MailboxesForm = ( { recordContinueEvent( { canContinue: true } ); setIsAddingToCart( true ); - const selectedSiteSlug = selectedSite?.slug ?? ''; - let checkoutPath = '/checkout/' + selectedSiteSlug; - - if ( isUnderDomainManagementAll( currentRoute ) ) { - const newEmail = mailboxOperations.mailboxes[ 0 ].getAsCartItem().email; - const redirectTo = isUnderDomainSiteContext( currentRoute ) - ? `${ domainSiteContextRoot() }/email/${ selectedDomainName }/${ selectedSiteSlug }?new-email=${ newEmail }` - : `${ domainManagementAllEmailRoot() }/${ selectedDomainName }/${ selectedSiteSlug }?new-email=${ newEmail }`; - - checkoutPath += '?redirect_to=' + encodeURIComponent( redirectTo ); - } + const checkoutPath = getEmailCheckoutPath( + selectedSite?.slug ?? '', + selectedDomainName, + currentRoute, + mailboxOperations.mailboxes[ 0 ].getAsCartItem().email + ); cartManager .addProductsToCart( [ getCartItems( mailboxOperations.mailboxes, mailProperties ) ] ) diff --git a/client/my-sites/email/email-management/email-home.tsx b/client/my-sites/email/email-management/email-home.tsx index 58da9b57f2967e..f5974e7498b8c8 100644 --- a/client/my-sites/email/email-management/email-home.tsx +++ b/client/my-sites/email/email-management/email-home.tsx @@ -149,7 +149,9 @@ const EmailHome = ( props: EmailManagementHomeProps ) => { if ( ! domainHasEmail( selectedDomain ) ) { return ( +
{ showBackButton && ( async ( mailboxOperations: MailboxOperations ) => { setAddingToCart( true ); @@ -93,10 +96,17 @@ const getOnSubmitNewMailboxesHandler = ) : getEmailProductPropertiesForUpsell( emailProduct, numberOfMailboxes ); + const checkoutPath = getEmailCheckoutPath( + siteSlug, + domain.name, + currentRoute, + mailboxOperations.mailboxes[ 0 ].getAsCartItem().email + ); + shoppingCartManager .addProductsToCart( [ getCartItems( mailboxOperations.mailboxes, emailProperties ) ] ) .then( () => { - page( '/checkout/' + siteSlug ); + page( checkoutPath ); } ) .finally( () => setAddingToCart( false ) ) .catch( () => { diff --git a/client/my-sites/email/email-providers-comparison/stacked/provider-cards/google-workspace-card.tsx b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/google-workspace-card.tsx index a08ea5a8e10888..5066cc7a5736ad 100644 --- a/client/my-sites/email/email-providers-comparison/stacked/provider-cards/google-workspace-card.tsx +++ b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/google-workspace-card.tsx @@ -25,6 +25,7 @@ import { EmailProvider } from 'calypso/my-sites/email/form/mailboxes/types'; import { usePasswordResetEmailField } from 'calypso/my-sites/email/hooks/use-password-reset-email-field'; import { useDispatch, useSelector } from 'calypso/state'; import canUserPurchaseGSuite from 'calypso/state/selectors/can-user-purchase-gsuite'; +import getCurrentRoute from 'calypso/state/selectors/get-current-route'; import { getDomainsBySiteId } from 'calypso/state/sites/domains/selectors'; import { getSelectedSite } from 'calypso/state/ui/selectors'; import type { TranslateResult } from 'i18n-calypso'; @@ -73,6 +74,7 @@ const GoogleWorkspaceCard = ( props: EmailProvidersStackedCardProps ) => { domains, selectedDomainName: selectedDomainName, } ); + const currentRoute = useSelector( getCurrentRoute ); const cartKey = useCartKey(); const dispatch = useDispatch(); @@ -123,6 +125,7 @@ const GoogleWorkspaceCard = ( props: EmailProvidersStackedCardProps ) => { setAddingToCart, shoppingCartManager, siteSlug, + currentRoute, } ); googleWorkspace.onExpandedChange = onExpandedChange; diff --git a/client/my-sites/email/email-providers-comparison/stacked/provider-cards/professional-email-card.tsx b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/professional-email-card.tsx index 56d15c49953da5..47bbd16a392d46 100644 --- a/client/my-sites/email/email-providers-comparison/stacked/provider-cards/professional-email-card.tsx +++ b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/professional-email-card.tsx @@ -23,6 +23,7 @@ import { import { EmailProvider } from 'calypso/my-sites/email/form/mailboxes/types'; import { usePasswordResetEmailField } from 'calypso/my-sites/email/hooks/use-password-reset-email-field'; import { useDispatch, useSelector } from 'calypso/state'; +import getCurrentRoute from 'calypso/state/selectors/get-current-route'; import { getDomainsBySiteId } from 'calypso/state/sites/domains/selectors'; import { getSelectedSite } from 'calypso/state/ui/selectors'; import type { EmailProvidersStackedCardProps, ProviderCardProps } from './provider-card-props'; @@ -81,6 +82,7 @@ const ProfessionalEmailCard = ( props: EmailProvidersStackedCardProps ) => { domains, selectedDomainName: selectedDomainName, } ); + const currentRoute = useSelector( getCurrentRoute ); const provider = EmailProvider.Titan; const emailProduct = useSelector( ( state ) => @@ -123,6 +125,7 @@ const ProfessionalEmailCard = ( props: EmailProvidersStackedCardProps ) => { setAddingToCart, shoppingCartManager, siteSlug, + currentRoute, } ); professionalEmail.formFields = ( diff --git a/client/my-sites/email/paths.ts b/client/my-sites/email/paths.ts index cb7d7dc2bb3d9e..e472a8b85ebd3a 100644 --- a/client/my-sites/email/paths.ts +++ b/client/my-sites/email/paths.ts @@ -5,6 +5,7 @@ import { isUnderDomainSiteContext, domainManagementRoot, domainSiteContextRoot, + domainManagementAllEmailRoot, } from 'calypso/my-sites/domains/paths'; type QueryStringParameters = { [ key: string ]: string | undefined }; @@ -245,5 +246,28 @@ export const getProfessionalEmailCheckoutUpsellPath = ( receiptId: number | string ) => `/checkout/offer-professional-email/${ domainName }/${ receiptId }/${ siteName }`; +export const getEmailCheckoutPath = ( + siteName: string, + domainName: string, + relativeTo?: string, + newEmail?: string +): string => { + let checkoutPath = '/checkout/' + siteName; + + if ( isUnderDomainManagementAll( relativeTo ) ) { + let redirectTo = isUnderDomainSiteContext( relativeTo ) + ? `${ domainSiteContextRoot() }/email/${ domainName }/${ siteName }` + : `${ domainManagementAllEmailRoot() }/${ domainName }/${ siteName }`; + + if ( newEmail ) { + redirectTo += `?new-email=${ newEmail }`; + } + + checkoutPath += '?redirect_to=' + encodeURIComponent( redirectTo ); + } + + return checkoutPath; +}; + export const getMailboxesPath = ( siteName?: string | null ) => siteName ? `/mailboxes/${ siteName }` : `/mailboxes`; diff --git a/client/my-sites/email/test/paths.ts b/client/my-sites/email/test/paths.ts index f0e5bb941973fd..c858539f49d976 100644 --- a/client/my-sites/email/test/paths.ts +++ b/client/my-sites/email/test/paths.ts @@ -20,6 +20,7 @@ import { getProfessionalEmailCheckoutUpsellPath, getMailboxesPath, isUnderEmailManagementAll, + getEmailCheckoutPath, } from '../paths'; const siteName = 'hello.wordpress.com'; @@ -217,6 +218,31 @@ describe( 'path helper functions', () => { ); } ); + it( 'getEmailCheckoutPath', () => { + const email = 'hi@example.com'; + const relativeToDomainManagement = '/domains/manage/all/email'; + const relativeToSiteDomain = '/overview/site-domain/email'; + + expect( getEmailCheckoutPath( siteName, domainName ) ).toEqual( `/checkout/${ siteName }` ); + expect( getEmailCheckoutPath( siteName, domainName, relativeToDomainManagement ) ).toEqual( + `/checkout/${ siteName }?redirect_to=${ encodeURIComponent( + `${ relativeToDomainManagement }/${ domainName }/${ siteName }` + ) }` + ); + expect( + getEmailCheckoutPath( siteName, domainName, relativeToDomainManagement, email ) + ).toEqual( + `/checkout/${ siteName }?redirect_to=${ encodeURIComponent( + `${ relativeToDomainManagement }/${ domainName }/${ siteName }?new-email=${ email }` + ) }` + ); + expect( getEmailCheckoutPath( siteName, domainName, relativeToSiteDomain, email ) ).toEqual( + `/checkout/${ siteName }?redirect_to=${ encodeURIComponent( + `${ relativeToSiteDomain }/${ domainName }/${ siteName }?new-email=${ email }` + ) }` + ); + } ); + it.each( [ [ '/domains', false ], [ '/email', false ], diff --git a/client/my-sites/plans/jetpack-plans/controller.tsx b/client/my-sites/plans/jetpack-plans/controller.tsx index 7fcf225bc63eb3..85d9c9a09fba99 100644 --- a/client/my-sites/plans/jetpack-plans/controller.tsx +++ b/client/my-sites/plans/jetpack-plans/controller.tsx @@ -7,7 +7,6 @@ import isJetpackCloud from 'calypso/lib/jetpack/is-jetpack-cloud'; import { getSelectedSiteId } from 'calypso/state/ui/selectors'; import { getSlugInTerm } from './convert-slug-terms'; import getParamsFromContext from './get-params-from-context'; -import JetpackCompletePage from './jetpack-complete-page'; import { getPlanRecommendationFromContext } from './plan-upgrade/utils'; import SelectorPage from './selector'; import { StoragePricing } from './storage-pricing'; @@ -89,15 +88,6 @@ export const productSelect = next(); }; -export const offerJetpackComplete: Callback = ( context, next ) => { - const { site } = context.params; - const urlQueryArgs: QueryArgs = context.query; - context.primary = ( - - ); - next(); -}; - export const jetpackFreeWelcome: Callback = ( context, next ) => { context.primary = ; next(); diff --git a/client/my-sites/plans/jetpack-plans/index.ts b/client/my-sites/plans/jetpack-plans/index.ts index 11c56791dbcd9c..eb6f8964f3ff72 100644 --- a/client/my-sites/plans/jetpack-plans/index.ts +++ b/client/my-sites/plans/jetpack-plans/index.ts @@ -1,16 +1,15 @@ -import config, { isEnabled } from '@automattic/calypso-config'; +import { isEnabled } from '@automattic/calypso-config'; import page, { type Callback } from '@automattic/calypso-router'; import { makeLayout, render as clientRender } from 'calypso/controller/index.web'; import { jetpackBoostWelcome, jetpackFreeWelcome, jetpackSocialWelcome, - offerJetpackComplete, productSelect, } from './controller'; export default function ( rootUrl: string, ...rest: Callback[] ): void { - const addBoostAndSocialRoutes = config.isEnabled( 'jetpack/pricing-add-boost-social' ); + const addBoostAndSocialRoutes = isEnabled( 'jetpack/pricing-add-boost-social' ); page( `${ rootUrl }/jetpack-free/welcome`, jetpackFreeWelcome, makeLayout, clientRender ); @@ -19,18 +18,6 @@ export default function ( rootUrl: string, ...rest: Callback[] ): void { page( `${ rootUrl }/jetpack-social/welcome`, jetpackSocialWelcome, makeLayout, clientRender ); } - // We provide access to the page only when Feature Flag is enabled - if ( isEnabled( 'jetpack/offer-complete-after-activation' ) ) { - // Offer jetpack complete after Jetpack plugin activation - page( - `${ rootUrl }/complete/:site?/:lang?`, - ...rest, - offerJetpackComplete, - makeLayout, - clientRender - ); - } - page( `${ rootUrl }/:duration?/:site?`, ...rest, diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/index.tsx deleted file mode 100644 index 8a5cba54a751cd..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { PLAN_JETPACK_COMPLETE } from '@automattic/calypso-products'; -import { useTranslate } from 'i18n-calypso'; -import { useCallback } from 'react'; -import Button from 'calypso/components/forms/form-button'; -import { useDispatch, useSelector } from 'calypso/state'; -import { recordTracksEvent } from 'calypso/state/analytics/actions'; -import getJetpackRecommendationsUrl from 'calypso/state/selectors/get-jetpack-recommendations-url'; -import { getSelectedSiteSlug, getSelectedSiteId } from 'calypso/state/ui/selectors'; -import { buildCheckoutURL } from '../../../get-purchase-url-callback'; - -import './style.scss'; - -const CtaButtons = () => { - const translate = useTranslate(); - const siteSlug = useSelector( getSelectedSiteSlug ); - const siteId = useSelector( getSelectedSiteId ); - const dispatch = useDispatch(); - - const onGetCompleteClick = useCallback( () => { - dispatch( - recordTracksEvent( 'calypso_jetpack_complete_page_get_complete_click', { - site_id: siteId, - } ) - ); - }, [ dispatch, siteId ] ); - - const onContinueFreeClick = useCallback( () => { - dispatch( - recordTracksEvent( 'calypso_jetpack_complete_page_continue_free_click', { - site_id: siteId, - } ) - ); - }, [ dispatch, siteId ] ); - - const checkoutURL = siteSlug - ? buildCheckoutURL( siteSlug, PLAN_JETPACK_COMPLETE ) - : `/checkout/${ PLAN_JETPACK_COMPLETE }`; - - const stayFreeURL = useSelector( - ( state ) => getJetpackRecommendationsUrl( state ) ?? 'https://jetpack.com' - ); - - return ( -
- - -
- ); -}; - -export default CtaButtons; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/style.scss deleted file mode 100644 index 0ec1fc70bc8f9c..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/components/cta-buttons/style.scss +++ /dev/null @@ -1,59 +0,0 @@ -.jetpack-complete-page__cta-buttons { - display: inline-flex; - align-items: center; - width: 100%; - flex-direction: column; - @include breakpoint-deprecated( ">960px" ) { - flex-direction: row; - gap: 24px; - } - .button { - border: 1px solid; - border-radius: 4px; - font-weight: 600; - min-width: 276px; - @include breakpoint-deprecated( ">960px" ) { - min-width: 164px; - } - } - .jetpack-complete-page__get-complete-button { - &.form-button { - color: var(--studio-white); - background-color: var(--studio-black); - border-color: var(--studio-black); - &:hover, - &:active { - background-color: var(--studio-gray-60); - border-color: var(--studio-black); - } - &:focus { - background-color: var(--studio-gray-60); - border-color: var(--studio-white); - box-shadow: 0 0 0 1px var(--studio-black) !important; - } - } - } - .jetpack-complete-page__start-free-button { - margin-block-start: 24px; - @include breakpoint-deprecated( ">960px" ) { - margin-block-start: unset; - } - &.form-button { - color: var(--studio-black); - background-color: var(--studio-white); - border-color: var(--studio-black); - &:hover, - &:active { - background-color: var(--studio-gray-5); - color: var(--studio-black); - border-color: var(--studio-black); - } - &:focus { - background-color: var(--studio-gray-5); - color: var(--studio-black); - border-color: var(--studio-white); - box-shadow: 0 0 0 1px var(--studio-black) !important; - } - } - } -} diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/hooks/use-individual-peoducts.ts b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/hooks/use-individual-peoducts.ts deleted file mode 100644 index 9c86bad1f6aaf0..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/hooks/use-individual-peoducts.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - PRODUCT_JETPACK_ANTI_SPAM, - PRODUCT_JETPACK_BACKUP_T1_MONTHLY, - PRODUCT_JETPACK_BOOST, - PRODUCT_JETPACK_CRM, - PRODUCT_JETPACK_SCAN, - PRODUCT_JETPACK_SEARCH, - PRODUCT_JETPACK_SOCIAL_ADVANCED, - PRODUCT_JETPACK_VIDEOPRESS, -} from '@automattic/calypso-products'; -import { useMemo } from 'react'; -import slugToSelectorProduct from '../../slug-to-selector-product'; -import { SelectorProduct } from '../../types'; - -const PRODUCTS = [ - PRODUCT_JETPACK_BACKUP_T1_MONTHLY, - PRODUCT_JETPACK_BOOST, - PRODUCT_JETPACK_SCAN, - PRODUCT_JETPACK_SEARCH, - PRODUCT_JETPACK_ANTI_SPAM, - PRODUCT_JETPACK_SOCIAL_ADVANCED, - PRODUCT_JETPACK_VIDEOPRESS, - PRODUCT_JETPACK_CRM, -]; - -export const useIndividualProducts = () => - useMemo( () => PRODUCTS.map( slugToSelectorProduct ) as SelectorProduct[], [] ); diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/index.tsx deleted file mode 100644 index 1ce2b8f3051081..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -//TODO: Remove this eslnit exception when whole component/child components are finished. -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { PLAN_JETPACK_COMPLETE } from '@automattic/calypso-products'; -import { useTranslate } from 'i18n-calypso'; -import { useEffect } from 'react'; -import rnaImageComplete from 'calypso/assets/images/jetpack/rna-image-complete.png'; -import rnaImageComplete2xRetina from 'calypso/assets/images/jetpack/rna-image-complete@2x.png'; -import QueryIntroOffers from 'calypso/components/data/query-intro-offers'; -import QueryJetpackSaleCoupon from 'calypso/components/data/query-jetpack-sale-coupon'; -import QueryJetpackUserLicensesCounts from 'calypso/components/data/query-jetpack-user-licenses-counts'; -import QueryProductsList from 'calypso/components/data/query-products-list'; -import QuerySiteProducts from 'calypso/components/data/query-site-products'; -import JetpackRnaDialogCard from 'calypso/components/jetpack/card/jetpack-rna-dialog-card'; -import Main from 'calypso/components/main'; -import { JPC_PATH_PLANS } from 'calypso/jetpack-connect/constants'; -import slugToSelectorProduct from 'calypso/my-sites/plans/jetpack-plans/slug-to-selector-product'; -import { useSelector, useDispatch } from 'calypso/state'; -import { recordTracksEvent } from 'calypso/state/analytics/actions'; -import { successNotice } from 'calypso/state/notices/actions'; -import getSelectedSiteId from 'calypso/state/ui/selectors/get-selected-site-id'; -import { QueryArgs } from '../types'; -import CtaButtons from './components/cta-buttons'; -import { ItemPrice } from './item-price'; -import ItemsIncluded from './items-included'; -import ProductHeader from './product-header'; -import ShowLicenseActivationLinkIfAvailable from './show-license-activation-link-if-available'; -import PricingPageLink from './view-all-products-link'; - -import './style.scss'; - -interface Props { - urlQueryArgs?: QueryArgs; - siteSlug?: string; -} - -const JetpackCompletePage: React.FC< Props > = ( { urlQueryArgs, siteSlug } ) => { - const translate = useTranslate(); - const dispatch = useDispatch(); - const siteId = useSelector( getSelectedSiteId ); - const item = slugToSelectorProduct( PLAN_JETPACK_COMPLETE ); - - useEffect( () => { - if ( window.location.pathname.startsWith( JPC_PATH_PLANS ) ) { - dispatch( successNotice( translate( 'Jetpack is successfully connected' ) ) ); - } - - dispatch( - recordTracksEvent( 'calypso_jetpack_complete_page_open', { - site_id: siteId, - } ) - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [] ); - - return ( - <> - - - { siteId && } - { siteId && } - - -
- - - - - - - - - -
- - ); -}; - -export default JetpackCompletePage; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/index.tsx deleted file mode 100644 index 61fe8d45e3895d..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import DisplayPrice from 'calypso/components/jetpack/card/jetpack-product-card/display-price'; -import { SelectorProduct } from 'calypso/my-sites/plans/jetpack-plans/types'; -import useItemPrice from 'calypso/my-sites/plans/jetpack-plans/use-item-price'; -import { useSelector } from 'calypso/state'; -import { getCurrentUserCurrencyCode } from 'calypso/state/currency-code/selectors'; - -import './style.scss'; - -interface Props { - item: SelectorProduct | null; - siteId: number | null; -} - -export const ItemPrice: React.FC< Props > = ( { item, siteId } ) => { - const { - originalPrice, - discountedPrice, - discountedPriceDuration, - isFetching: pricesAreFetching, - } = useItemPrice( siteId, item, item?.monthlyProductSlug || '' ); - const currencyCode = useSelector( getCurrentUserCurrencyCode ); - - return ( - item && ( -
- -
- ) - ); -}; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/style.scss deleted file mode 100644 index 2cdb076c5e5782..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/item-price/style.scss +++ /dev/null @@ -1,57 +0,0 @@ -@import "@wordpress/base-styles/variables"; -@import "@wordpress/base-styles/breakpoints"; -@import "@wordpress/base-styles/mixins"; - -.item-price__complete .display-price { - font-family: Inter, $sans; - margin-bottom: 32px; - - @include break-medium { - margin-bottom: 24px; - } - - .plan-price.is-discounted, - .plan-price.is-original { - - .plan-price__currency-symbol { - font-size: $font-title-medium; - font-weight: 400; - line-height: 20px; - margin-top: 0; - } - - .plan-price__integer { - font-family: "SF Pro Display", $sans; - line-height: 40px; - } - - .plan-price__fraction { - line-height: $font-title-medium; - vertical-align: super; - font-size: 0.75rem; - } - } -} - -.item-price__complete .display-price:not(.is-placeholder) { - .plan-price.is-discounted { - color: var(--studio-gray-100); - } - - .plan-price.is-original { - color: var(--studio-gray-20); - } -} - -.item-price__complete .display-price__billing-time-frame { - display: table; - margin-top: 12px; - @include break-medium { - margin-bottom: 7px; - } -} - -.item-price__complete .display-price.is-placeholder .plan-price { - transform: unset; - transform: none; -} diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/index.tsx deleted file mode 100644 index 1fb90d976d9929..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useIndividualProducts } from '../hooks/use-individual-peoducts'; -import SingleProduct from './single-product'; - -const ItemsIncluded = () => { - const products = useIndividualProducts(); - return ( -
    - { products.map( ( product ) => ( -
  • - -
  • - ) ) } -
- ); -}; - -export default ItemsIncluded; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/single-product.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/single-product.tsx deleted file mode 100644 index ebf26fdcb96b9e..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/single-product.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useTranslate } from 'i18n-calypso'; -import { useCallback } from 'react'; -import InfoPopover from 'calypso/components/info-popover'; -import { useDispatch, useSelector } from 'calypso/state'; -import { recordTracksEvent } from 'calypso/state/analytics/actions'; -import { getSelectedSiteId } from 'calypso/state/ui/selectors'; -import getProductIcon from '../../product-store/utils/get-product-icon'; -import { SelectorProduct } from '../../types'; -import './style.scss'; -import { getProductUrl } from './utils'; - -type Props = { - product: SelectorProduct; -}; - -const SingleProduct: React.FC< Props > = ( { product } ) => { - const translate = useTranslate(); - const siteId = useSelector( getSelectedSiteId ); - const dispatch = useDispatch(); - - const onLinkClick = useCallback( () => { - dispatch( - recordTracksEvent( 'calypso_jetpack_complete_page_single_product_open_link', { - site_id: siteId, - link: getProductUrl( product.productSlug ), - product_slug: product.productSlug, - } ) - ); - }, [ dispatch, siteId, product.productSlug ] ); - - const onTooltipOpen = useCallback( () => { - dispatch( - recordTracksEvent( 'calypso_jetpack_complete_page_single_product_open_tooltip', { - site_id: siteId, - product_slug: product.productSlug, - } ) - ); - }, [ dispatch, siteId, product.productSlug ] ); - - return ( -
- ); -}; - -export default SingleProduct; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/style.scss deleted file mode 100644 index 78856a5bfa6a50..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/style.scss +++ /dev/null @@ -1,66 +0,0 @@ -@import "@wordpress/base-styles/breakpoints"; - -.single-product__wrapper { - display: inline-flex; - align-items: center; -} - -.single-product__icon { - width: 2.4rem; - height: 2.4rem; - background: linear-gradient(159.87deg, #f6f6f4 7.24%, #f7f4ea 64.73%, #ddedd5 116.53%); - display: flex; - align-items: center; - justify-content: center; -} - -.single-product__text { - font-weight: 600; - font-style: normal; - margin-left: 0.5rem; - font-size: 0.875rem; -} - -.single-product__info { - margin-left: 0.75rem; - - button { - display: flex; - } -} - -.single-product__info-popover-wrapper { - color: var(--color-neutral-100); - - hr { - margin: 0.75rem 0; - } -} - -.single-product__info-link { - font-style: normal; - font-weight: 400; - line-height: 24px; - - display: flex; - align-items: center; - letter-spacing: -0.01em; - text-decoration-line: underline; - - color: var(--color-neutral-100) !important; -} - -.items-included__list-table { - column-gap: 40px; - display: grid; - list-style-type: none; - margin: 0; - row-gap: 1rem; - padding-bottom: 45px; -} - -@media (min-width: $break-medium) { - .items-included__list-table { - grid-template-columns: repeat(2, 1fr); - } -} diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/utils.ts b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/utils.ts deleted file mode 100644 index 25654cc9d91583..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/items-included/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - PRODUCT_JETPACK_BACKUP_T1_MONTHLY, - JETPACK_BACKUP_PRODUCT_LANDING_PAGE_URL, - JETPACK_ANTI_SPAM_PRODUCT_LANDING_PAGE_URL, - JETPACK_SCAN_PRODUCT_LANDING_PAGE_URL, - JETPACK_SEARCH_PRODUCT_LANDING_PAGE_URL, - PRODUCT_JETPACK_ANTI_SPAM, - PRODUCT_JETPACK_BOOST, - PRODUCT_JETPACK_CRM, - PRODUCT_JETPACK_SCAN, - PRODUCT_JETPACK_SEARCH, - PRODUCT_JETPACK_SOCIAL_ADVANCED, - PRODUCT_JETPACK_VIDEOPRESS, - JETPACK_BOOST_PRODUCT_LANDING_PAGE_URL, - JETPACK_SOCIAL_PRODUCT_LANDING_PAGE_URL, - JETPACK_VIDEOPRESS_PRODUCT_LANDING_PAGE_URL, - JETPACK_CRM_PRODUCT_LANDING_PAGE_URL, -} from '@automattic/calypso-products'; -import { localizeUrl } from '@automattic/i18n-utils'; - -const PRODUCT_URL_MAP: { [ key: string ]: string } = { - [ PRODUCT_JETPACK_BACKUP_T1_MONTHLY ]: JETPACK_BACKUP_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_BOOST ]: JETPACK_BOOST_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_SCAN ]: JETPACK_SCAN_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_SEARCH ]: JETPACK_SEARCH_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_ANTI_SPAM ]: JETPACK_ANTI_SPAM_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_SOCIAL_ADVANCED ]: JETPACK_SOCIAL_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_VIDEOPRESS ]: JETPACK_VIDEOPRESS_PRODUCT_LANDING_PAGE_URL, - [ PRODUCT_JETPACK_CRM ]: JETPACK_CRM_PRODUCT_LANDING_PAGE_URL, -}; - -export const getProductUrl = ( productSlug: string ) => - localizeUrl( PRODUCT_URL_MAP[ productSlug ] ); diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/index.tsx deleted file mode 100644 index 7828eb15929470..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useTranslate } from 'i18n-calypso'; -import * as React from 'react'; -import { preventWidows } from 'calypso/lib/formatting'; -import { SelectorProduct } from 'calypso/my-sites/plans/jetpack-plans/types'; - -import './style.scss'; - -type Props = { - item: SelectorProduct | null; -}; - -const ProductHeader: React.FC< Props > = ( { item } ) => { - const translate = useTranslate(); - - if ( item && item?.displayName && item?.lightboxDescription ) { - return ( -
-

- { translate( 'Get Jetpack %(productName)s', { - args: { productName: item.displayName }, - } ) } -

-

{ preventWidows( item.lightboxDescription ) }

-
- ); - } - return null; -}; - -export default ProductHeader; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/style.scss deleted file mode 100644 index cca5764046f641..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/product-header/style.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import "@automattic/typography/styles/variables"; - -.product-header { - font-family: "SF Pro Display", $sans; - color: var(--studio-black); - .product-header__heading { - font-family: inherit; - font-size: $font-headline-small; - line-height: 40px; - font-weight: 700; - margin-bottom: 16px; - } - - .product-header__sub-heading { - font-family: inherit; - font-size: $font-title-small; - line-height: 30px; - font-weight: 500; - margin-bottom: 1rem; - } -} diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/show-license-activation-link-if-available.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/show-license-activation-link-if-available.tsx deleted file mode 100644 index 0953aec3905c8a..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/show-license-activation-link-if-available.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useMobileBreakpoint } from '@automattic/viewport-react'; -import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; -import { useCallback, useEffect } from 'react'; -import { useDispatch, useSelector } from 'calypso/state'; -import { recordTracksEvent } from 'calypso/state/analytics/actions'; -import { getSiteAdminUrl } from 'calypso/state/sites/selectors'; -import { - getUserLicensesCounts, - hasFetchedUserLicensesCounts, -} from 'calypso/state/user-licensing/selectors'; - -import './style.scss'; - -interface Props { - siteId: number | null; -} - -function ShowLicenseActivationLinkIfAvailable( { siteId }: Props ) { - const translate = useTranslate(); - const dispatch = useDispatch(); - const siteAdminUrl = useSelector( ( state ) => getSiteAdminUrl( state, siteId ) ); - const jetpackDashboardUrl = siteAdminUrl + 'admin.php?page=jetpack#/license/activation'; - const userLicensesCounts = useSelector( getUserLicensesCounts ); - const hasFetchedLicensesCounts = useSelector( hasFetchedUserLicensesCounts ); - const hasDetachedLicenses = userLicensesCounts && userLicensesCounts[ 'detached' ] !== 0; - const isMobile = useMobileBreakpoint(); - - useEffect( () => { - if ( siteId && hasDetachedLicenses ) { - dispatch( - recordTracksEvent( 'calypso_post_connection_complete_page_license_link_render', { - site_id: siteId, - } ) - ); - } - }, [ dispatch, hasDetachedLicenses, siteId ] ); - - const onLinkClick = useCallback( () => { - dispatch( - recordTracksEvent( 'calypso_post_connection_complete_page_license_link_clicked', { - site_id: siteId, - } ) - ); - }, [ dispatch, siteId ] ); - - if ( ! hasFetchedLicensesCounts && ! userLicensesCounts ) { - return ( -
-
- { isMobile ? ( - Searching for any license keys... - ) : ( - Searching for unused product license keys... - ) } -
-
- ); - } - - if ( siteId && hasDetachedLicenses ) { - return ( -
-
- { isMobile ? ( - - { translate( 'Activate license' ) } - - ) : ( - <> - { translate( 'Already have an existing plan or license key? ' ) } - - - { translate( 'Click here to get started' ) } - - - - ) } -
-
- ); - } - - return null; -} - -export default ShowLicenseActivationLinkIfAvailable; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/style.scss deleted file mode 100644 index 56e20858e1d333..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/style.scss +++ /dev/null @@ -1,68 +0,0 @@ -@import "calypso/assets/stylesheets/shared/mixins/breakpoints"; -@import "@automattic/onboarding/styles/mixins"; -@import "@wordpress/base-styles/breakpoints"; - -.layout.is-section-jetpack-connect:not(.is-woocommerce-core-profiler-flow) { - background-color: #f6f6f6; - #content.layout__content { - padding: 44px 17px 26px 18px; - @include breakpoint-deprecated( ">960px" ) { - padding-top: 35px; - } - } -} - -#notices.global-notices { - top: 82.1px; -} - -// The only way to do "mobile first" responsive layouts in Calypso is using (using the build in @inclides & @mixins) is to use: -// @include breakpoint-depreciated()??? -// @mixin break-large (medium, small, etc), they all utilize @media (max-width: {size}) which is "desktop first".. Hmmm? -// vs min-width media queries, which is "mobile first" responsive design. May have to create a P2 about this.. -@include breakpoint-deprecated( "<960px" ) { - #notices.global-notices { - top: 44px; - right: 17px; - left: 18px; - } - .notice { - width: calc(100% - 35px); - } -} - -.main.is-wide-layout.jetpack-complete-page__main { - padding-top: 71px; - max-width: 1129px; - @include breakpoint-deprecated( ">960px" ) { - padding-top: 0; - } -} - -.show-license-activation-link-if-available__container { - display: flex; - - .show-license-activation-link-if-available { - display: inline-block; - margin-left: auto; - color: var(--studio-black); - a { - font-weight: 400; - line-height: 24px; - letter-spacing: -2%; - color: var(--studio-black); - text-decoration: underline; - &:hover, - &:active { - text-decoration: none; - } - } - &.is-placeholder { - @include placeholder(--studio-gray-5); - color: var(--studio-gray-50); - span { - padding: 1px 8px; - } - } - } -} diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/test/index.js b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/test/index.js deleted file mode 100644 index eb21c2001f50ba..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/test/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { screen } from '@testing-library/react'; -import { renderWithProvider } from 'calypso/test-helpers/testing-library'; -import JetpackCompletePage from '../index'; - -jest.mock( 'calypso/state/ui/selectors/get-selected-site-id', () => jest.fn() ); - -jest.mock( 'calypso/my-sites/plans/jetpack-plans/use-item-price', () => - jest.fn().mockReturnValue( { - originalPrice: 10, - discountedPrice: 5, - discountedPriceDuration: 1, - isFetching: false, - } ) -); - -jest.mock( 'calypso/state/products-list/selectors', () => ( { - isProductsListFetching: jest.fn().mockReturnValue( false ), -} ) ); - -const renderWithRedux = ( el ) => renderWithProvider( el, {} ); - -describe( 'jetpack complete page', () => { - it( 'page renders without errors with buttons', async () => { - renderWithRedux( ); - - expect( screen.getByText( 'Get Complete' ) ).toBeTruthy(); - expect( screen.getByText( 'Start for free' ) ).toBeTruthy(); - } ); -} ); diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/index.tsx b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/index.tsx deleted file mode 100644 index 36548a6e04b5cf..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { recordTracksEvent } from '@automattic/calypso-analytics'; -import { useTranslate } from 'i18n-calypso'; - -import './style.scss'; - -type Props = { - siteSlug?: string; -}; - -const ViewAllProductsLink: React.FC< Props > = ( { siteSlug } ) => { - const translate = useTranslate(); - - return ( - recordTracksEvent( 'calypso_view_individual_products_link_click' ) } - href={ `https://cloud.jetpack.com/pricing/${ siteSlug }` } - > - { translate( 'View individual products' ) } - - ); -}; - -export default ViewAllProductsLink; diff --git a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/style.scss b/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/style.scss deleted file mode 100644 index 03c4a563649c8c..00000000000000 --- a/client/my-sites/plans/jetpack-plans/jetpack-complete-page/view-all-products-link/style.scss +++ /dev/null @@ -1,16 +0,0 @@ -a.view-all-products-link { - display: inline-block; - margin-bottom: 40px; - - font-family: "SF Pro Display", $sans; - font-size: $font-body; - font-weight: 600; - line-height: 24px; - color: var(--studio-black); - text-decoration: underline; - - &:hover, - &:active { - text-decoration: none; - } -} diff --git a/client/my-sites/plugins/index.web.js b/client/my-sites/plugins/index.web.js index 1c06ce5fa3cdbb..06ef9b08ba9422 100644 --- a/client/my-sites/plugins/index.web.js +++ b/client/my-sites/plugins/index.web.js @@ -7,7 +7,7 @@ import { render as clientRender, redirectIfCurrentUserCannot, } from 'calypso/controller'; -import { navigation, siteSelection, sites } from 'calypso/my-sites/controller'; +import { noSite, navigation, siteSelection, sites } from 'calypso/my-sites/controller'; import { renderPluginsDashboard, browsePlugins, @@ -126,6 +126,7 @@ export default function ( router ) { scrollTopIfNoHash, navigation, redirectTrialSites, + noSite, renderPluginsSidebar, renderPluginsDashboard, makeLayout, @@ -139,6 +140,7 @@ export default function ( router ) { scrollTopIfNoHash, navigation, redirectTrialSites, + noSite, renderPluginsSidebar, renderPluginsDashboard, makeLayout, diff --git a/client/my-sites/plugins/plugins-dashboard/index.tsx b/client/my-sites/plugins/plugins-dashboard/index.tsx index 8479a4cb63075b..2e9a642c1be916 100644 --- a/client/my-sites/plugins/plugins-dashboard/index.tsx +++ b/client/my-sites/plugins/plugins-dashboard/index.tsx @@ -41,8 +41,7 @@ import { import { removePluginStatuses } from 'calypso/state/plugins/installed/status/actions'; import { getAllPlugins as getAllWporgPlugins } from 'calypso/state/plugins/wporg/selectors'; import { getProductsList } from 'calypso/state/products-list/selectors'; -import getSelectedOrAllSites from 'calypso/state/selectors/get-selected-or-all-sites'; -import getSelectedOrAllSitesWithPlugins from 'calypso/state/selectors/get-selected-or-all-sites-with-plugins'; +import getSites from 'calypso/state/selectors/get-sites'; import { isRequestingSites } from 'calypso/state/sites/selectors'; import { PluginActionName, PluginActions, Site } from '../hooks/types'; import { withShowPluginActionDialog } from '../hooks/use-show-plugin-action-dialog'; @@ -108,9 +107,8 @@ const PluginsDashboard = ( { const dispatch = useDispatch(); const translate = useTranslate(); const isJetpackCloudOrA8CForAgencies = isJetpackCloud() || isA8CForAgencies(); - const allSites = useSelector( ( state ) => getSelectedOrAllSites( state ) ); - const sites = useSelector( ( state ) => getSelectedOrAllSitesWithPlugins( state ) ); - const siteIds = siteObjectsToSiteIds( sites ) ?? []; + const allSites = useSelector( ( state ) => getSites( state ) ); + const siteIds = siteObjectsToSiteIds( allSites ) ?? []; const wporgPlugins = useSelector( ( state ) => getAllWporgPlugins( state ) ); const isLoading = useSelector( ( state ) => isRequestingForAllSites( state ) || isRequestingSites( state ) @@ -170,7 +168,7 @@ const PluginsDashboard = ( { ?.filter( ( plugin: Plugin ) => ! isDeactivatingOrRemovingAndJetpackSelected( plugin ) ) .map( ( p: Plugin ) => { return Object.keys( p.sites ).map( ( siteId ) => { - const site = sites.find( ( s ) => s?.ID === parseInt( siteId ) ); + const site = allSites.find( ( s ) => s?.ID === parseInt( siteId ) ); return { plugin: p, site, @@ -308,7 +306,7 @@ const PluginsDashboard = ( { showPluginActionDialog( actionName as PluginActionName, pluginsToProcess, - sites as Site[], + allSites as Site[], selectedActionCallback( pluginsToProcess ) ); }; diff --git a/client/my-sites/site-settings/settings-controller.js b/client/my-sites/site-settings/settings-controller.js index ea4544c59d8930..e6557ac9cd4624 100644 --- a/client/my-sites/site-settings/settings-controller.js +++ b/client/my-sites/site-settings/settings-controller.js @@ -1,5 +1,6 @@ import page from '@automattic/calypso-router'; import titlecase from 'to-title-case'; +import { redirectIfDuplicatedView } from 'calypso/controller'; import { recordPageView } from 'calypso/lib/analytics/page-view'; import { navigate } from 'calypso/lib/navigate'; import { getIsRemoveDuplicateViewsExperimentEnabled } from 'calypso/lib/remove-duplicate-views-experiment'; @@ -54,7 +55,11 @@ export const redirectToolsIfRemoveDuplicateViewsExperimentEnabled = async ( cont if ( ! URL_MAP[ slug ] ) { return next(); } - return page.redirect( `/sites/settings/site/${ context.params.site_id }/${ URL_MAP[ slug ] }` ); + + const queryParams = context.querystring ? `?${ context.querystring }` : ''; + return page.redirect( + `/sites/settings/site/${ context.params.site_id }/${ URL_MAP[ slug ] }${ queryParams }` + ); } next(); @@ -93,7 +98,7 @@ export async function redirectGeneralSettingsIfDuplicatedViewsEnabled( context, return page.redirect( `/sites/settings/site/${ siteSlug }` ); } - next(); + redirectIfDuplicatedView( 'options-general.php' )( context, next ); } export async function siteSettings( context, next ) { diff --git a/client/my-sites/site-settings/settings-discussion/index.js b/client/my-sites/site-settings/settings-discussion/index.js index d35d32a77c1a9f..50db7f3a47dc35 100644 --- a/client/my-sites/site-settings/settings-discussion/index.js +++ b/client/my-sites/site-settings/settings-discussion/index.js @@ -1,5 +1,5 @@ import page from '@automattic/calypso-router'; -import { makeLayout, render as clientRender } from 'calypso/controller'; +import { makeLayout, render as clientRender, redirectIfDuplicatedView } from 'calypso/controller'; import { navigation, siteSelection } from 'calypso/my-sites/controller'; import { siteSettings } from 'calypso/my-sites/site-settings/settings-controller'; import { discussion } from './controller'; @@ -8,6 +8,7 @@ export default function () { page( '/settings/discussion/:site_id', siteSelection, + redirectIfDuplicatedView( 'options-discussion.php' ), navigation, siteSettings, discussion, diff --git a/client/my-sites/site-settings/settings-newsletter/main.tsx b/client/my-sites/site-settings/settings-newsletter/main.tsx index 9a1d06921d7e90..9f591bbabad8e8 100644 --- a/client/my-sites/site-settings/settings-newsletter/main.tsx +++ b/client/my-sites/site-settings/settings-newsletter/main.tsx @@ -43,6 +43,7 @@ type Fields = { wpcom_featured_image_in_email?: boolean; wpcom_newsletter_categories?: number[]; wpcom_newsletter_categories_enabled?: boolean; + wpcom_newsletter_categories_modal_hidden?: boolean; wpcom_subscription_emails_use_excerpt?: boolean; jetpack_subscriptions_reply_to?: string; jetpack_subscriptions_from_name?: string; @@ -69,6 +70,7 @@ const getFormSettings = ( settings?: Fields ) => { wpcom_featured_image_in_email, wpcom_newsletter_categories, wpcom_newsletter_categories_enabled, + wpcom_newsletter_categories_modal_hidden, wpcom_subscription_emails_use_excerpt, jetpack_subscriptions_reply_to, jetpack_subscriptions_from_name, @@ -90,6 +92,7 @@ const getFormSettings = ( settings?: Fields ) => { wpcom_featured_image_in_email: !! wpcom_featured_image_in_email, wpcom_newsletter_categories: wpcom_newsletter_categories || [], wpcom_newsletter_categories_enabled: !! wpcom_newsletter_categories_enabled, + wpcom_newsletter_categories_modal_hidden: !! wpcom_newsletter_categories_modal_hidden, wpcom_subscription_emails_use_excerpt: !! wpcom_subscription_emails_use_excerpt, jetpack_subscriptions_reply_to: jetpack_subscriptions_reply_to || '', jetpack_subscriptions_from_name: jetpack_subscriptions_from_name || '', @@ -325,6 +328,7 @@ const NewsletterSettingsForm = wrapSettingsForm( getFormSettings )( ( { disabled={ disabled } newsletterCategoryIds={ fields.wpcom_newsletter_categories || defaultNewsletterCategoryIds } newsletterCategoriesEnabled={ fields.wpcom_newsletter_categories_enabled } + newsletterCategoriesModalHiddenEnabled={ fields.wpcom_newsletter_categories_modal_hidden } handleToggle={ handleToggle } updateFields={ updateFields } /> diff --git a/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-hide-modal-toggle.tsx b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-hide-modal-toggle.tsx new file mode 100644 index 00000000000000..7eda42187f48b4 --- /dev/null +++ b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-hide-modal-toggle.tsx @@ -0,0 +1,40 @@ +import { ToggleControl } from '@wordpress/components'; +import { useTranslate } from 'i18n-calypso'; +import FormSettingExplanation from 'calypso/components/forms/form-setting-explanation'; + +export const NEWSLETTER_CATEGORIES_MODAL_HIDDEN_ENABLED_OPTION = + 'wpcom_newsletter_categories_modal_hidden'; + +type NewsletterCategoriesHideModalToggleProps = { + disabled?: boolean; + value?: boolean; + handleToggle: ( field: string ) => ( value: boolean ) => void; +}; + +const NewsletterCategoriesHideModalToggle = ( { + disabled, + handleToggle, + value = false, +}: NewsletterCategoriesHideModalToggleProps ) => { + const translate = useTranslate(); + + return ( +
+ + + { translate( 'After subscribing, new users skip the category selection modal.' ) + + ' ' + + translate( + 'Instead, you can manually assign the default categories within each subscriber block.' + ) } + +
+ ); +}; + +export default NewsletterCategoriesHideModalToggle; diff --git a/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-section.tsx b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-section.tsx index 4ce017bc27b79d..080fcc86989c10 100644 --- a/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-section.tsx +++ b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-section.tsx @@ -1,3 +1,4 @@ +import config from '@automattic/calypso-config'; import { Card } from '@automattic/components'; import clsx from 'clsx'; import { useTranslate } from 'i18n-calypso'; @@ -5,6 +6,7 @@ import React from 'react'; import TermTreeSelector from 'calypso/blocks/term-tree-selector'; import FormLegend from 'calypso/components/forms/form-legend'; import FormSettingExplanation from 'calypso/components/forms/form-setting-explanation'; +import NewsletterCategoriesHideModalToggle from './newsletter-categories-hide-modal-toggle'; import NewsletterCategoriesToggle from './newsletter-categories-toggle'; import './style.scss'; @@ -12,6 +14,7 @@ type NewsletterCategoriesSectionProps = { disabled?: boolean; handleToggle: ( field: string ) => ( value: boolean ) => void; newsletterCategoriesEnabled?: boolean; + newsletterCategoriesModalHiddenEnabled?: boolean; newsletterCategoryIds: number[]; updateFields: ( fields: { [ key: string ]: unknown } ) => void; }; @@ -20,6 +23,7 @@ const NewsletterCategoriesSection = ( { disabled, handleToggle, newsletterCategoriesEnabled, + newsletterCategoriesModalHiddenEnabled, newsletterCategoryIds, updateFields, }: NewsletterCategoriesSectionProps ) => { @@ -78,6 +82,25 @@ const NewsletterCategoriesSection = ( { ) } + + { config.isEnabled( 'newsletter-categories-section' ) && ( + + + + ) } ); }; diff --git a/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/style.scss b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/style.scss index 66ae08fe66fd47..ae8d62b55561ab 100644 --- a/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/style.scss +++ b/client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/style.scss @@ -26,6 +26,12 @@ } } +.newsletter-categories-settings__hide-modal-toggle { + &.hidden { + display: none; + } +} + .newsletter-categories-settings__description { &.form-setting-explanation { margin-top: -12px; diff --git a/client/my-sites/site-settings/settings-reading/index.ts b/client/my-sites/site-settings/settings-reading/index.ts index b616fa30d668f8..cff0139eb199a1 100644 --- a/client/my-sites/site-settings/settings-reading/index.ts +++ b/client/my-sites/site-settings/settings-reading/index.ts @@ -1,5 +1,5 @@ import page from '@automattic/calypso-router'; -import { makeLayout, render as clientRender } from 'calypso/controller'; +import { makeLayout, render as clientRender, redirectIfDuplicatedView } from 'calypso/controller'; import { navigation, siteSelection } from 'calypso/my-sites/controller'; import { siteSettings } from 'calypso/my-sites/site-settings/settings-controller'; import { createReadingSettings } from './controller'; @@ -8,6 +8,7 @@ export default function () { page( '/settings/reading/:site_id', siteSelection, + redirectIfDuplicatedView( 'options-reading.php' ), navigation, siteSettings, createReadingSettings, diff --git a/client/my-sites/site-settings/settings-writing/index.js b/client/my-sites/site-settings/settings-writing/index.js index 3c0c55602df135..46699387ca4dc3 100644 --- a/client/my-sites/site-settings/settings-writing/index.js +++ b/client/my-sites/site-settings/settings-writing/index.js @@ -19,6 +19,7 @@ export default function () { page( '/settings/writing/:site_id', siteSelection, + _redirectIfDuplicatedView( 'options-writing.php' ), navigation, siteSettings, writing, diff --git a/client/my-sites/stats/features/modules/stats-locations/stats-locations.tsx b/client/my-sites/stats/features/modules/stats-locations/stats-locations.tsx index d8d9669eab3cba..05eeb44cde478c 100644 --- a/client/my-sites/stats/features/modules/stats-locations/stats-locations.tsx +++ b/client/my-sites/stats/features/modules/stats-locations/stats-locations.tsx @@ -15,6 +15,8 @@ import StatsListCard from 'calypso/my-sites/stats/stats-list/stats-list-card'; import StatsModulePlaceholder from 'calypso/my-sites/stats/stats-module/placeholder'; import { trackStatsAnalyticsEvent } from 'calypso/my-sites/stats/utils'; import { useSelector } from 'calypso/state'; +import getEnvStatsFeatureSupportChecks from 'calypso/state/sites/selectors/get-env-stats-feature-supports'; +import { getSiteStatsNormalizedData } from 'calypso/state/stats/lists/selectors'; import { getSelectedSiteId } from 'calypso/state/ui/selectors'; import EmptyModuleCard from '../../../components/empty-module-card/empty-module-card'; import { SUPPORT_URL, JETPACK_SUPPORT_URL_TRAFFIC } from '../../../const'; @@ -25,6 +27,7 @@ import { STATS_FEATURE_LOCATION_CITY_VIEWS, } from '../../../constants'; import Geochart from '../../../geochart'; +import StatsCardUpdateJetpackVersion from '../../../stats-card-upsell/stats-card-update-jetpack-version'; import StatsCardSkeleton from '../shared/stats-card-skeleton'; import StatsInfoArea from '../shared/stats-info-area'; import CountryFilter from './country-filter'; @@ -96,17 +99,29 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary const shouldGateTab = useShouldGateStats( optionLabels[ selectedOption ].feature ); const shouldGate = shouldGateStatsModule || shouldGateTab; const geoMode = GEO_MODES[ selectedOption ]; - const title = optionLabels[ selectedOption ]?.selectLabel; + const title = translate( 'Locations' ); + + const { supportsLocationsStats: supportsLocationsStatsFeature } = useSelector( ( state ) => + getEnvStatsFeatureSupportChecks( state, siteId ) + ); // Main location data query const { - data = [], + data: locationsViewsData = [], isLoading: isRequestingData, isError, } = useLocationViewsQuery< StatsLocationViewsData >( siteId, geoMode, query, countryFilter, { - enabled: ! shouldGate, + enabled: ! shouldGate && supportsLocationsStatsFeature, } ); + // The legacy endpoint that only supports countries (not regions or cities) + // will be used when the new Locations Stats feature is not available. + const legacyCountriesViewsData = useSelector( ( state ) => + getSiteStatsNormalizedData( state, siteId, statType, query ) + ) as [ id: number, label: string ]; + + const data = supportsLocationsStatsFeature ? locationsViewsData : legacyCountriesViewsData; + // Only fetch separate countries list if we're not already in country tab // This is to avoid fetching the same data twice. const { data: countriesList = [] } = useLocationViewsQuery< StatsLocationViewsData >( @@ -115,7 +130,7 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary query, null, { - enabled: ! shouldGate && geoMode !== 'country', + enabled: ! shouldGate && supportsLocationsStatsFeature && geoMode !== 'country', } ); @@ -203,7 +218,7 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary const titleTooltip = ( - { translate( 'Stats on visitors and their {{link}}viewing location{{/link}}.', { + { translate( 'Visitors {{link}}viewing location{{/link}} by countries, regions and cities.', { comment: '{{link}} links to support documentation.', components: { link: ( @@ -219,9 +234,13 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary ); - const hasLocationData = Array.isArray( data ) && data.length > 0; + const showJetpackUpgradePrompt = geoMode !== 'country' && ! supportsLocationsStatsFeature; - const locationData = shouldGate ? sampleLocations : data; + const showUpsell = shouldGate || showJetpackUpgradePrompt; + + const locationData = showUpsell ? sampleLocations : data; + + const hasLocationData = Array.isArray( locationData ) && locationData.length > 0; const heroElement = ( <> @@ -238,6 +257,22 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary ); + const getModuleOverlay = () => { + if ( shouldGate ) { + return ( + + ); + } + + if ( showJetpackUpgradePrompt ) { + return ; + } + + return null; + }; + + const moduleOverlay = getModuleOverlay(); + return ( <> { ! shouldGateStatsModule && siteId && statType && ( @@ -289,21 +324,14 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary : undefined } onShowMoreClick={ onShowMoreClick } - overlay={ - shouldGate && ( - - ) - } + overlay={ moduleOverlay } /> ) } { ! isRequestingData && ! hasLocationData && ! shouldGate && ( // show empty state , + content: ( + + { numberFormat( viewsBestDayTotal, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } + + ), footer: translate( '%(percent)s of views', { args: { percent: formatPercentage( bestViewsEverPercent, true ) }, context: 'Stats: Percentage of views', @@ -251,7 +254,7 @@ function AllTimeStatsCard( { infoItems, siteId }: AllTimeStatsCardProps ) { className="highlight-card-info-item-count" title={ Number.isFinite( info.count ) ? String( info.count ) : undefined } > - { formattedNumber( info.count ) } + { numberFormat( info.count ) }
); diff --git a/client/my-sites/stats/site.jsx b/client/my-sites/stats/site.jsx index e4be8976926cca..79b3ba896956b0 100644 --- a/client/my-sites/stats/site.jsx +++ b/client/my-sites/stats/site.jsx @@ -202,7 +202,6 @@ function StatsBody( { siteId, chartTab = 'views', date, context, isInternal, ... supportsPlanUsage, supportsUTMStats: supportsUTMStatsFeature, supportsDevicesStats: supportsDevicesStatsFeature, - supportsLocationsStats: supportsLocationsStatsFeature, isOldJetpack, supportUserFeedback, } = useSelector( ( state ) => getEnvStatsFeatureSupportChecks( state, siteId ) ); @@ -253,7 +252,6 @@ function StatsBody( { siteId, chartTab = 'views', date, context, isInternal, ... const shouldShowUpsells = isOdysseyStats && ! isAtomic; const supportsUTMStats = supportsUTMStatsFeature || isInternal; const supportsDevicesStats = supportsDevicesStatsFeature || isInternal; - const supportsLocationsStats = supportsLocationsStatsFeature || isInternal; const getAvailableLegend = () => { const activeTab = getActiveTab( chartTab ); // TODO: remove this when we support hourly visitors. @@ -635,7 +633,7 @@ function StatsBody( { siteId, chartTab = 'views', date, context, isInternal, ... className={ halfWidthModuleClasses } /> - { config.isEnabled( 'stats/locations' ) && supportsLocationsStats ? ( + { config.isEnabled( 'stats/locations' ) ? ( <> = ( { className, siteId, { translate( 'Update Jetpack plugin' ) } } + icon="info-outline" /> ); }; diff --git a/client/my-sites/stats/stats-card-upsell/stats-card-upsell-overlay.tsx b/client/my-sites/stats/stats-card-upsell/stats-card-upsell-overlay.tsx index f3d9f2bb327aaf..ced57b6653e4af 100644 --- a/client/my-sites/stats/stats-card-upsell/stats-card-upsell-overlay.tsx +++ b/client/my-sites/stats/stats-card-upsell/stats-card-upsell-overlay.tsx @@ -9,6 +9,7 @@ interface Props { onClick: ( event: React.MouseEvent< HTMLButtonElement, MouseEvent > ) => void; buttonLabel?: string | TranslateResult; buttonComponent?: React.ReactNode; + icon?: string; } const StatsCardUpsell: React.FC< Props > = ( { @@ -17,6 +18,7 @@ const StatsCardUpsell: React.FC< Props > = ( { copyText, buttonLabel, buttonComponent, + icon = 'lock', } ) => { const translate = useTranslate(); @@ -24,7 +26,7 @@ const StatsCardUpsell: React.FC< Props > = ( {
- +

{ copyText }

{ buttonComponent ? ( diff --git a/client/my-sites/stats/stats-email-top-row/top-card.jsx b/client/my-sites/stats/stats-email-top-row/top-card.jsx index 72b4b75248821a..bb0c0a722a0587 100644 --- a/client/my-sites/stats/stats-email-top-row/top-card.jsx +++ b/client/my-sites/stats/stats-email-top-row/top-card.jsx @@ -1,5 +1,5 @@ -import { Card, ShortenedNumber, Spinner } from '@automattic/components'; -import { useTranslate } from 'i18n-calypso'; +import { Card, Spinner } from '@automattic/components'; +import { useTranslate, numberFormat } from 'i18n-calypso'; /* This is a very stripped down version of HighlightCard * HighlightCard doesn't support non-numeric values @@ -25,7 +25,12 @@ const TopCardValue = ( { value, isLoading } ) => { return ( - + { numberFormat( value, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } ); }; diff --git a/client/my-sites/stats/stats-plan-usage/index.tsx b/client/my-sites/stats/stats-plan-usage/index.tsx index b444f226a0e594..ba9dafde57bb87 100644 --- a/client/my-sites/stats/stats-plan-usage/index.tsx +++ b/client/my-sites/stats/stats-plan-usage/index.tsx @@ -1,6 +1,5 @@ -import { formattedNumber } from '@automattic/components'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import React from 'react'; import usePlanUsageQuery from 'calypso/my-sites/stats/hooks/use-plan-usage-query'; @@ -86,8 +85,8 @@ const PlanUsage: React.FC< PlanUsageProps > = ( {
{ translate( '%(numberOfUsage)s / %(numberOfLimit)s views', { args: { - numberOfUsage: formattedNumber( usage ), - numberOfLimit: formattedNumber( limit ), + numberOfUsage: numberFormat( usage ), + numberOfLimit: typeof limit === 'number' ? numberFormat( limit ) : '-', }, } ) }
diff --git a/client/my-sites/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx b/client/my-sites/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx index b98bb4b64d330e..18bdd33cefec78 100644 --- a/client/my-sites/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx +++ b/client/my-sites/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx @@ -1,4 +1,5 @@ -import { Card, ShortenedNumber, Spinner } from '@automattic/components'; +import { Card, Spinner } from '@automattic/components'; +import { numberFormat } from 'i18n-calypso'; import { ReactNode } from 'react'; import InfoPopover from 'calypso/components/info-popover'; import '@automattic/components/src/highlight-cards/style.scss'; @@ -41,7 +42,14 @@ const SubscriberStatsCardValue = ( { className="highlight-card-count-value subscriber-stats-card__value" title={ String( value ) } > - + { typeof value === 'number' + ? numberFormat( value, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) + : '-' } ); }; diff --git a/client/reader/onboarding/style.scss b/client/reader/onboarding/style.scss index af0db23c492f46..161d9552e4999e 100644 --- a/client/reader/onboarding/style.scss +++ b/client/reader/onboarding/style.scss @@ -1,28 +1,17 @@ -@import "@wordpress/base-styles/breakpoints"; .reader-onboarding { - display: flex; - flex-wrap: wrap; - background-color: var(--color-surface-1); + background-color: var( --color-surface-1 ); border-radius: 6px; /* stylelint-disable-line scales/radii */ - box-shadow: rgba(0, 0, 0, 0.05) 0 0 0 1px inset; + box-shadow: rgba( 0, 0, 0, 0.05 ) 0 0 0 1px inset; margin-bottom: 24px; - padding: 10px; - gap: 20px; + padding: 20px 20px 5px 20px; h2 { font-weight: 600; - margin-top: 20px; + clear: initial; } - &__steps-column { - flex-grow: 2; - } - - @media ( min-width: $break-mobile ) { - padding: 20px; - } - - @media ( min-width: $break-huge ) { - gap: 40px; + .circular__progress-bar { + float: right; + margin: 5px 5px 5px 10px; } } diff --git a/client/server/middleware/assets.js b/client/server/middleware/assets.js index 986dc03e475fe8..983aeb61fad13f 100644 --- a/client/server/middleware/assets.js +++ b/client/server/middleware/assets.js @@ -1,4 +1,4 @@ -import { readFile } from 'fs/promises'; +import { open } from 'fs/promises'; import path from 'path'; import asyncHandler from 'express-async-handler'; import { defaults, groupBy } from 'lodash'; @@ -21,14 +21,30 @@ const getAssetType = ( asset ) => { const groupAssetsByType = ( assets ) => defaults( groupBy( assets, getAssetType ), EMPTY_ASSETS ); export default () => { - let assetsFile; - async function readAssets() { - if ( ! assetsFile ) { - assetsFile = JSON.parse( await readFile( ASSETS_FILE, 'utf8' ) ); + let assetsFile = null; + let assetsFileModified = 0; + async function doReadAssets() { + const fd = await open( ASSETS_FILE ); + const stats = await fd.stat(); + if ( ! assetsFile || stats.mtimeMs > assetsFileModified ) { + assetsFile = JSON.parse( await fd.readFile( 'utf8' ) ); + assetsFileModified = stats.mtimeMs; } + await fd.close(); return assetsFile; } + let checking = null; + function readAssets() { + if ( ! checking ) { + checking = doReadAssets().finally( () => { + checking = null; + } ); + } + + return checking; + } + return asyncHandler( async ( req, res, next ) => { const assets = await readAssets(); diff --git a/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx b/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx index 6f531f9a0ee21a..6d3f04f4b2b183 100644 --- a/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx +++ b/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx @@ -90,7 +90,7 @@ export default function InstallPlugins( { return; } - setProgress( progress + 0.2 ); + setProgress( progress + 20 ); dispatch( requestAtomicSoftwareStatus( siteId, 'woo-on-plans' ) ); }, !! installFailed || softwareApplied ? null : 3000 @@ -104,7 +104,7 @@ export default function InstallPlugins( { if ( softwareApplied ) { trackRedirect(); - setProgress( 1 ); + setProgress( 100 ); // Allow progress bar to complete setTimeout( () => { window.location.assign( wcAdminUrl ); diff --git a/client/signup/steps/woocommerce-install/transfer/transfer-site.tsx b/client/signup/steps/woocommerce-install/transfer/transfer-site.tsx index 596d87f64e28eb..56c3b9fcb9b1f0 100644 --- a/client/signup/steps/woocommerce-install/transfer/transfer-site.tsx +++ b/client/signup/steps/woocommerce-install/transfer/transfer-site.tsx @@ -92,21 +92,21 @@ export default function TransferSite( { switch ( transferStatus ) { case transferStates.PENDING: - setProgress( 0.2 ); + setProgress( 20 ); break; case transferStates.ACTIVE: - setProgress( 0.4 ); + setProgress( 40 ); break; case transferStates.PROVISIONED: - setProgress( 0.5 ); + setProgress( 50 ); break; case transferStates.COMPLETED: - setProgress( 0.7 ); + setProgress( 70 ); break; } if ( isTransferringStatusFailed || transferStatus === transferStates.ERROR ) { - setProgress( 1 ); + setProgress( 100 ); setTransferFailed( true ); onFailure( { @@ -133,7 +133,7 @@ export default function TransferSite( { if ( softwareApplied ) { trackRedirect(); - setProgress( 1 ); + setProgress( 100 ); // Allow progress bar to complete setTimeout( () => { window.location.assign( wcAdminUrl ); diff --git a/client/state/global-sidebar/selectors.ts b/client/state/global-sidebar/selectors.ts index 5168558e5cecdc..dfeecad2a0ae8f 100644 --- a/client/state/global-sidebar/selectors.ts +++ b/client/state/global-sidebar/selectors.ts @@ -28,6 +28,9 @@ const SITE_DASHBOARD_ROUTES = [ '/domains/manage/all/overview', '/domains/manage/all/email', '/domains/manage/all/contact-info', + + // Bulk Plugins management + '/plugins/manage/sites', ]; function isInRoute( state: AppState, routes: string[] ) { diff --git a/config/a8c-for-agencies-development.json b/config/a8c-for-agencies-development.json index 310e3d2b83290c..cd17d7260f0399 100644 --- a/config/a8c-for-agencies-development.json +++ b/config/a8c-for-agencies-development.json @@ -46,7 +46,8 @@ "a4a-migrations-page-v2": true, "a8c-for-agencies-agency-tier": true, "a4a-pressable-referrals": true, - "a4a-updated-add-new-site": true + "a4a-updated-add-new-site": true, + "a4a-wc-asia-signup-enabled": true }, "enable_all_sections": false, "sections": { diff --git a/config/a8c-for-agencies-horizon.json b/config/a8c-for-agencies-horizon.json index 1ce052f46e5d04..186080d3ca5589 100644 --- a/config/a8c-for-agencies-horizon.json +++ b/config/a8c-for-agencies-horizon.json @@ -39,7 +39,8 @@ "a4a-migrations-page-v2": true, "a8c-for-agencies-agency-tier": true, "a4a-pressable-referrals": true, - "a4a-updated-add-new-site": true + "a4a-updated-add-new-site": true, + "a4a-wc-asia-signup-enabled": true }, "enable_all_sections": false, "sections": { diff --git a/config/development.json b/config/development.json index 46554005f5a285..1b579894b964c1 100644 --- a/config/development.json +++ b/config/development.json @@ -102,7 +102,6 @@ "jetpack/simplify-pricing-structure": false, "jetpack/social-plans-v1": true, "jetpack/standalone-plugin-onboarding-update-v1": true, - "jetpack/offer-complete-after-activation": false, "jetpack/zendesk-chat-for-logged-in-users": true, "jitms": true, "lasagna": true, diff --git a/config/horizon.json b/config/horizon.json index c97c4ccf97dea5..0059065f4e036a 100644 --- a/config/horizon.json +++ b/config/horizon.json @@ -64,7 +64,6 @@ "jetpack/simplify-pricing-structure": false, "jetpack/social-plans-v1": true, "jetpack/standalone-plugin-onboarding-update-v1": true, - "jetpack/offer-complete-after-activation": false, "jitms": true, "lasagna": true, "launchpad-updates": false, diff --git a/config/production.json b/config/production.json index 46e08bf8a589b6..e7ce22247c1dd4 100644 --- a/config/production.json +++ b/config/production.json @@ -79,7 +79,6 @@ "jetpack/social-plans-v1": true, "jetpack/standalone-plugin-onboarding-update-v1": true, "jetpack-social/advanced-plan": false, - "jetpack/offer-complete-after-activation": false, "jetpack/zendesk-chat-for-logged-in-users": true, "jitms": true, "lasagna": true, diff --git a/config/stage.json b/config/stage.json index 8157daddaa51d6..81b50d0fc80d05 100644 --- a/config/stage.json +++ b/config/stage.json @@ -76,7 +76,6 @@ "jetpack/social-plans-v1": true, "jetpack/standalone-plugin-onboarding-update-v1": true, "jetpack-social/advanced-plan": true, - "jetpack/offer-complete-after-activation": false, "jetpack/zendesk-chat-for-logged-in-users": true, "jitms": true, "lasagna": true, diff --git a/config/test.json b/config/test.json index 9f0a5079fd12a4..5ea47c345e4043 100644 --- a/config/test.json +++ b/config/test.json @@ -62,7 +62,6 @@ "jetpack/sharing-buttons-block-enabled": false, "jetpack/simplify-pricing-structure": false, "jetpack-social/advanced-plan": false, - "jetpack/offer-complete-after-activation": false, "jitms": true, "lasagna": false, "launchpad-updates": false, diff --git a/config/wpcalypso.json b/config/wpcalypso.json index 64149c9d79923b..154af23dbaf9de 100644 --- a/config/wpcalypso.json +++ b/config/wpcalypso.json @@ -79,7 +79,6 @@ "jetpack/sharing-buttons-block-enabled": false, "jetpack/simplify-pricing-structure": false, "jetpack-social/advanced-plan": false, - "jetpack/offer-complete-after-activation": false, "jetpack/zendesk-chat-for-logged-in-users": true, "jitms": true, "lasagna": true, @@ -106,7 +105,7 @@ "migration-flow/experiment": false, "migration-flow/introductory-offer": true, "network-connection": true, - "newsletter-categories-section": false, + "newsletter-categories-section": true, "onboarding/import": true, "onboarding/import-from-blogger": true, "onboarding/import-from-medium": true, diff --git a/packages/calypso-e2e/src/lib/components/editor-welcome-guide-component.ts b/packages/calypso-e2e/src/lib/components/editor-welcome-guide-component.ts new file mode 100644 index 00000000000000..90211a9f7bdd49 --- /dev/null +++ b/packages/calypso-e2e/src/lib/components/editor-welcome-guide-component.ts @@ -0,0 +1,40 @@ +import { Page } from 'playwright'; +import { EditorComponent } from './editor-component'; + +const selectors = { + welcomeGuideWrapper: '.edit-post-welcome-guide', + // Welcome guide + welcomeGuideCloseButton: 'button[aria-label="Close"]', +}; + +/** + * Represents the welcome guide that shows in a popover when the editor loads. + */ +export class EditorWelcomeGuideComponent { + private page: Page; + private editor: EditorComponent; + + /** + * Constructs an instance of the component. + * + * @param {Page} page The underlying page. + * @param {EditorComponent} editor The EditorComponent instance. + */ + constructor( page: Page, editor: EditorComponent ) { + this.page = page; + this.editor = editor; + } + + /** + * close the welcome guide if needed. + */ + async closeWelcomeGuideIfNeeded(): Promise< void > { + const editorParent = await this.editor.parent(); + + const welcomGuideWrapper = editorParent.locator( selectors.welcomeGuideWrapper ); + await welcomGuideWrapper.waitFor( { state: 'visible' } ); + + const closeBtn = editorParent.locator( selectors.welcomeGuideCloseButton ); + await closeBtn.click(); + } +} diff --git a/packages/calypso-e2e/src/lib/components/index.ts b/packages/calypso-e2e/src/lib/components/index.ts index ce968888a8dbc9..71f96086925448 100644 --- a/packages/calypso-e2e/src/lib/components/index.ts +++ b/packages/calypso-e2e/src/lib/components/index.ts @@ -24,6 +24,7 @@ export * from './editor-gutenberg-component'; export * from './editor-block-list-view-component'; export * from './editor-sidebar-block-inserter-component'; export * from './editor-welcome-tour-component'; +export * from './editor-welcome-guide-component'; export * from './editor-popover-menu-component'; export * from './editor-site-styles-component'; export * from './editor-color-picker-component'; diff --git a/packages/calypso-e2e/src/lib/pages/editor-page.ts b/packages/calypso-e2e/src/lib/pages/editor-page.ts index 0f72675f313b8c..6a78f8293369a4 100644 --- a/packages/calypso-e2e/src/lib/pages/editor-page.ts +++ b/packages/calypso-e2e/src/lib/pages/editor-page.ts @@ -13,6 +13,7 @@ import { EditorInlineBlockInserterComponent, EditorSidebarBlockInserterComponent, EditorWelcomeTourComponent, + EditorWelcomeGuideComponent, EditorBlockToolbarComponent, EditorTemplateModalComponent, EditorPopoverMenuComponent, @@ -57,6 +58,7 @@ export class EditorPage { private editorSidebarBlockInserterComponent: EditorSidebarBlockInserterComponent; private editorInlineBlockInserterComponent: EditorInlineBlockInserterComponent; private editorWelcomeTourComponent: EditorWelcomeTourComponent; + private editorWelcomeGuideComponent: EditorWelcomeGuideComponent; private editorBlockToolbarComponent: EditorBlockToolbarComponent; private editorTemplateModalComponent: EditorTemplateModalComponent; private editorPopoverMenuComponent: EditorPopoverMenuComponent; @@ -78,6 +80,7 @@ export class EditorPage { this.editorPublishPanelComponent = new EditorPublishPanelComponent( page, this.editor ); this.editorBlockListViewComponent = new EditorBlockListViewComponent( page, this.editor ); this.editorWelcomeTourComponent = new EditorWelcomeTourComponent( page, this.editor ); + this.editorWelcomeGuideComponent = new EditorWelcomeGuideComponent( page, this.editor ); this.editorBlockToolbarComponent = new EditorBlockToolbarComponent( page, this.editor ); this.editorSidebarBlockInserterComponent = new EditorSidebarBlockInserterComponent( page, @@ -990,5 +993,12 @@ export class EditorPage { return this.editorToolbarComponent.openMoreOptionsMenu(); } + /** + * Close the + */ + async closeWelcomeGuideIfNeeded(): Promise< void > { + return this.editorWelcomeGuideComponent.closeWelcomeGuideIfNeeded(); + } + //#endregion } diff --git a/packages/calypso-e2e/src/lib/pages/signup/user-signup-page.ts b/packages/calypso-e2e/src/lib/pages/signup/user-signup-page.ts index 31b8b19c4fb196..ae72f0aa21e785 100644 --- a/packages/calypso-e2e/src/lib/pages/signup/user-signup-page.ts +++ b/packages/calypso-e2e/src/lib/pages/signup/user-signup-page.ts @@ -154,10 +154,13 @@ export class UserSignupPage { async signupWoo( email: string ): Promise< NewUserResponse > { await this.page.fill( selectors.emailInput, email ); - const [ , response ] = await Promise.all( [ - this.page.waitForURL( /.*woocommerce\.com*/, { waitUntil: 'networkidle' } ), - this.page.waitForResponse( /.*new\?.*/ ), + const [ response ] = await Promise.all( [ + this.page.waitForResponse( /.*users\/new\?.*/ ), this.page.click( selectors.submitButton ), + this.page.waitForURL( /.*woocommerce\.com*/, { + waitUntil: 'networkidle', + timeout: 25000, + } ), ] ); if ( ! response ) { diff --git a/packages/calypso-products/src/constants/jetpack.ts b/packages/calypso-products/src/constants/jetpack.ts index 830c788a9703af..2ba835a85c18c3 100644 --- a/packages/calypso-products/src/constants/jetpack.ts +++ b/packages/calypso-products/src/constants/jetpack.ts @@ -816,15 +816,6 @@ export const JETPACK_PRODUCT_CATEGORIES = < const >[ ]; // URL -export const JETPACK_BACKUP_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/upgrade/backup/'; -export const JETPACK_SEARCH_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/upgrade/search/'; -export const JETPACK_STATS_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/stats/'; -export const JETPACK_SCAN_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/upgrade/scan/'; -export const JETPACK_ANTI_SPAM_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/upgrade/anti-spam/'; -export const JETPACK_BOOST_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/boost/'; -export const JETPACK_SOCIAL_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/social/'; -export const JETPACK_VIDEOPRESS_PRODUCT_LANDING_PAGE_URL = 'https://jetpack.com/videopress/'; -export const JETPACK_CRM_PRODUCT_LANDING_PAGE_URL = 'https://jetpackcrm.com/'; // If JETPACK_CLOUD_REDIRECT_CHECKOUT_TO_WPADMIN is true, checkout will redirect to the site's wp-admin, // otherwise it will redirect to the JETPACK_REDIRECT_URL. Checkout references these constants in: // client/my-sites/checkout/src/hooks/use-get-thank-you-url/get-thank-you-page-url.ts diff --git a/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts b/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts index 6443d064ce91ca..874b40c825df63 100644 --- a/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts +++ b/packages/calypso-products/src/is-global-styles-on-personal-enabled.ts @@ -1,3 +1,5 @@ +import { isEnabled } from '@automattic/calypso-config'; + declare global { interface Window { isGlobalStylesOnPersonal?: boolean; @@ -7,5 +9,12 @@ export function isGlobalStylesOnPersonalEnabled(): boolean { if ( typeof window === 'undefined' ) { return false; } + + if ( window.isGlobalStylesOnPersonal ) { + return true; + } + + window.isGlobalStylesOnPersonal = isEnabled( 'global-styles/on-personal-plan' ); + return !! window.isGlobalStylesOnPersonal; } diff --git a/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx b/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx index 2a8e1362eb7794..52043f220abcc5 100644 --- a/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx +++ b/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx @@ -3,7 +3,6 @@ import { Icon, chevronDown, chevronUp, tag, file } from '@wordpress/icons'; import clsx from 'clsx'; import { numberFormat } from 'i18n-calypso'; import React, { Fragment, useState } from 'react'; -import ShortenedNumber from '../number-formatters'; import type { HorizontalBarListItemProps } from './types'; import './style.scss'; @@ -106,8 +105,18 @@ const HorizontalBarListItem = ( { const renderValue = () => { if ( useShortNumber ) { - return ; + return ( + + { numberFormat( value, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } + + ); } + if ( formatValue ) { return formatValue( value, data ); } diff --git a/packages/components/src/post-stats-card/index.tsx b/packages/components/src/post-stats-card/index.tsx index 50a630a486ae55..9e5a3c88ae3a98 100644 --- a/packages/components/src/post-stats-card/index.tsx +++ b/packages/components/src/post-stats-card/index.tsx @@ -1,9 +1,9 @@ import { recordTracksEvent } from '@automattic/calypso-analytics'; import { Icon, commentContent, starEmpty } from '@wordpress/icons'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import { useMemo } from 'react'; -import { Card, ShortenedNumber, Button } from '../'; +import { Card, Button } from '../'; import { eye } from '../icons'; import './style.scss'; @@ -78,21 +78,48 @@ export default function PostStatsCard( {
{ translate( 'Views' ) }
- + + { ! isLoading && viewCount !== null + ? numberFormat( viewCount, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) + : '-' } +
{ translate( 'Likes' ) }
- + + { ! isLoading && likeCount !== null + ? numberFormat( likeCount, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) + : '-' } +
{ translate( 'Comments' ) }
- + + { ! isLoading && commentCount !== null + ? numberFormat( commentCount, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) + : '-' } +
diff --git a/packages/data-stores/src/onboard/reducer.ts b/packages/data-stores/src/onboard/reducer.ts index 72f8c29e216c1c..32a2da9673a7ae 100644 --- a/packages/data-stores/src/onboard/reducer.ts +++ b/packages/data-stores/src/onboard/reducer.ts @@ -356,12 +356,12 @@ const pendingAction: Reducer< undefined | ( () => Promise< any > ), OnboardActio return state; }; -const progress: Reducer< number, OnboardAction > = ( state = -1, action ) => { +const progress: Reducer< number | undefined, OnboardAction > = ( state, action ) => { if ( action.type === 'SET_PROGRESS' ) { return action.progress; } if ( action.type === 'RESET_ONBOARD_STORE' ) { - return -1; + return undefined; } return state; }; diff --git a/packages/design-picker/src/components/style.scss b/packages/design-picker/src/components/style.scss index 8de01189b0567b..b21ec375e2453d 100644 --- a/packages/design-picker/src/components/style.scss +++ b/packages/design-picker/src/components/style.scss @@ -36,7 +36,7 @@ @include break-large { margin-bottom: 64px; } - + .design-picker__design-card-title { position: sticky; top: 0; @@ -66,7 +66,9 @@ .theme-card__info { margin-top: 8px; - height: auto; + &:not(:has(.theme-card__info-badge-container)) { + height: auto; + } @include break-small { margin-top: 12px; diff --git a/packages/design-picker/src/components/theme-card/style.scss b/packages/design-picker/src/components/theme-card/style.scss index 1696381fa79a12..12add3144ad99b 100644 --- a/packages/design-picker/src/components/theme-card/style.scss +++ b/packages/design-picker/src/components/theme-card/style.scss @@ -16,6 +16,7 @@ $theme-card-info-margin-top: 16px; .theme-card__image-container { box-shadow: 0 0 0 2px var(--color-primary); border-radius: 4px; + z-index: 10; } } @@ -64,7 +65,7 @@ $theme-card-info-margin-top: 16px; .theme-card--is-actionable { .theme-card__image { transition: transform 300ms ease-in-out; - + &:hover, &:focus { transform: scale(1.03); diff --git a/packages/help-center/src/components/help-center-more-resources.tsx b/packages/help-center/src/components/help-center-more-resources.tsx index b07f64fb850bc4..8ac55e46cc95ce 100644 --- a/packages/help-center/src/components/help-center-more-resources.tsx +++ b/packages/help-center/src/components/help-center-more-resources.tsx @@ -6,7 +6,7 @@ import WhatsNewGuide, { useWhatsNewAnnouncementsQuery } from '@automattic/whats- import { Button, SVG, Circle } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useState } from '@wordpress/element'; -import { Icon, captureVideo, formatListNumbered, external, institution } from '@wordpress/icons'; +import { Icon, formatListNumbered, external, institution } from '@wordpress/icons'; import { useI18n } from '@wordpress/react-i18n'; import { useHelpCenterContext } from '../contexts/HelpCenterContext'; import { NewReleases } from '../icons'; @@ -102,21 +102,6 @@ export const HelpCenterMoreResources = () => {
-
  • - -
  • *:not(:first-child) { + padding-top: 8px; + } + + > *:not(:last-child) { + padding-bottom: 8px; + } + + ol, ul { + padding-left: 20px; + padding-right: 20px; + } } } @@ -303,6 +323,7 @@ $blueberry-color: #3858e9; padding-top: 16px; margin-bottom: 0; margin-top: 0; + line-height: 20px; } .odie-introduction-message-content { @@ -419,7 +440,7 @@ $blueberry-color: #3858e9; .odie-sources-link { overflow-wrap: anywhere; - color: var( --studio-blue-40 ); + color: $blueberry-color; font-style: normal; font-weight: 600; padding: 4px 16px; @@ -436,7 +457,7 @@ $blueberry-color: #3858e9; margin: 0; .odie-sources-link { - padding: 0 4px; + padding: 0; } } diff --git a/packages/odie-client/src/data/use-get-zendesk-conversation.ts b/packages/odie-client/src/data/use-get-zendesk-conversation.ts index da099334d4af9c..385b376f4c95c9 100644 --- a/packages/odie-client/src/data/use-get-zendesk-conversation.ts +++ b/packages/odie-client/src/data/use-get-zendesk-conversation.ts @@ -1,6 +1,5 @@ import { useCallback } from 'react'; import Smooch from 'smooch'; -import { useOdieAssistantContext } from '../context'; import { zendeskMessageConverter } from '../utils'; import { useGetUnreadConversations } from './use-get-unread-conversations'; import type { ZendeskMessage } from '../types'; @@ -23,7 +22,6 @@ const parseResponse = ( conversation: Conversation ) => { */ export const useGetZendeskConversation = () => { const getUnreadNotifications = useGetUnreadConversations(); - const { trackEvent } = useOdieAssistantContext(); return useCallback( ( { @@ -46,19 +44,11 @@ export const useGetZendeskConversation = () => { return Number( conversation.metadata[ 'odieChatId' ] ) === Number( chatId ); } - return false; + throw new Error(); } ); if ( ! conversation ) { - // Conversation id was passed but the conversion was not found. Something went wrong. - if ( conversationId ) { - trackEvent( 'zendesk_conversation_not_found', { - conversationId, - chatId, - conversationsCount: conversations?.length ?? null, - } ); - } - return null; + throw new Error(); } // We need to ensure that more than one message is loaded @@ -70,6 +60,6 @@ export const useGetZendeskConversation = () => { } ) .catch( () => parseResponse( conversation ) ); }, - [ getUnreadNotifications, trackEvent ] + [ getUnreadNotifications ] ); }; diff --git a/packages/odie-client/src/hooks/use-get-combined-chat.ts b/packages/odie-client/src/hooks/use-get-combined-chat.ts index 7ae08496a38f9a..4fcba1979188ad 100644 --- a/packages/odie-client/src/hooks/use-get-combined-chat.ts +++ b/packages/odie-client/src/hooks/use-get-combined-chat.ts @@ -2,9 +2,10 @@ import { HelpCenterSelect } from '@automattic/data-stores'; import { HELP_CENTER_STORE } from '@automattic/help-center/src/stores'; import { useSelect } from '@wordpress/data'; import { useState, useEffect } from '@wordpress/element'; +import { v4 as uuidv4 } from 'uuid'; import { ODIE_TRANSFER_MESSAGE } from '../constants'; -import { emptyChat } from '../context'; -import { useGetZendeskConversation, useOdieChat } from '../data'; +import { emptyChat, useOdieAssistantContext } from '../context'; +import { useGetZendeskConversation, useManageSupportInteraction, useOdieChat } from '../data'; import type { Chat, Message } from '../types'; /** @@ -39,8 +40,9 @@ export const useGetCombinedChat = ( canConnectToZendesk: boolean ) => { const [ mainChatState, setMainChatState ] = useState< Chat >( emptyChat ); const getZendeskConversation = useGetZendeskConversation(); - const { data: odieChat, isLoading: isOdieChatLoading } = useOdieChat( Number( odieId ) ); + const { startNewInteraction } = useManageSupportInteraction(); + const { trackEvent } = useOdieAssistantContext(); useEffect( () => { if ( odieId && odieChat && ! conversationId ) { @@ -53,25 +55,38 @@ export const useGetCombinedChat = ( canConnectToZendesk: boolean ) => { } ); } else if ( conversationId && canConnectToZendesk ) { if ( isChatLoaded ) { - getZendeskConversation( { - chatId: odieChat?.odieId, - conversationId: conversationId.toString(), - } )?.then( ( conversation ) => { - if ( conversation ) { - setMainChatState( { - ...( odieChat ? odieChat : {} ), - supportInteractionId: currentSupportInteraction!.uuid, - conversationId: conversation.id, - messages: [ - ...( odieChat ? odieChat.messages : [] ), - ...( odieChat ? ODIE_TRANSFER_MESSAGE : [] ), - ...( conversation.messages as Message[] ), - ], - provider: 'zendesk', - status: currentSupportInteraction?.status === 'closed' ? 'closed' : 'loaded', - } ); - } - } ); + try { + getZendeskConversation( { + chatId: odieChat?.odieId, + conversationId: conversationId.toString(), + } )?.then( ( conversation ) => { + if ( conversation ) { + setMainChatState( { + ...( odieChat ? odieChat : {} ), + supportInteractionId: currentSupportInteraction!.uuid, + conversationId: conversation.id, + messages: [ + ...( odieChat ? odieChat.messages : [] ), + ...( odieChat ? ODIE_TRANSFER_MESSAGE : [] ), + ...( conversation.messages as Message[] ), + ], + provider: 'zendesk', + status: currentSupportInteraction?.status === 'closed' ? 'closed' : 'loaded', + } ); + } + } ); + } catch ( error ) { + // Conversation id was passed but the conversion was not found. Something went wrong. + trackEvent( 'zendesk_conversation_not_found', { + conversationId, + odieId, + } ); + + startNewInteraction( { + event_source: 'help-center', + event_external_id: uuidv4(), + } ); + } } } else if ( currentSupportInteraction ) { setMainChatState( ( prevChat ) => ( { diff --git a/packages/plans-grid-next/src/components/shared/storage/components/storage-dropdown.tsx b/packages/plans-grid-next/src/components/shared/storage/components/storage-dropdown.tsx index 84c6167a864292..d7f44d7450c19a 100644 --- a/packages/plans-grid-next/src/components/shared/storage/components/storage-dropdown.tsx +++ b/packages/plans-grid-next/src/components/shared/storage/components/storage-dropdown.tsx @@ -1,4 +1,4 @@ -import { AddOns, WpcomPlansUI } from '@automattic/data-stores'; +import { type AddOnMeta, AddOns, WpcomPlansUI } from '@automattic/data-stores'; import { CustomSelectControl } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { useCallback, useEffect, useMemo } from '@wordpress/element'; @@ -9,7 +9,7 @@ import DropdownOption from '../../../dropdown-option'; import useDefaultStorageOption from '../hooks/use-default-storage-option'; import usePlanStorage from '../hooks/use-plan-storage'; import useStorageString from '../hooks/use-storage-string'; -import type { PlanSlug } from '@automattic/calypso-products'; +import type { PlanSlug, WPComPlanStorageFeatureSlug } from '@automattic/calypso-products'; type StorageDropdownProps = { planSlug: PlanSlug; @@ -93,43 +93,49 @@ const StorageDropdown = ( { ( select ) => select( WpcomPlansUI.store ).getSelectedStorageOptionForPlan( planSlug, siteId ), [ planSlug, siteId ] ); - const defaultStorageOption = useDefaultStorageOption( { planSlug } ); + const defaultStorageOptionSlug = useDefaultStorageOption( { planSlug } ); const availableStorageAddOns = AddOns.useAvailableStorageAddOns( { siteId } ); const planStorage = usePlanStorage( planSlug ); useEffect( () => { if ( ! selectedStorageOptionForPlan ) { - defaultStorageOption && + defaultStorageOptionSlug && setSelectedStorageOptionForPlan( { - addOnSlug: defaultStorageOption, + addOnSlug: defaultStorageOptionSlug, planSlug, siteId, } ); } }, [ - defaultStorageOption, + defaultStorageOptionSlug, planSlug, selectedStorageOptionForPlan, setSelectedStorageOptionForPlan, siteId, ] ); - const defaultStorageItem = useMemo( - () => ( { - key: defaultStorageOption || '', - name: ( - - ) as unknown as string, - } ), - [ defaultStorageOption, planStorage ] - ); + const selectControlOptions = useMemo( () => { + // Get the default storage add-on meta or the storage included with the plan + let defaultStorageAddOnMeta: + | AddOnMeta + | { + addOnSlug: AddOns.StorageAddOnSlug | WPComPlanStorageFeatureSlug; + prices: AddOnMeta[ 'prices' ] | null; + quantity: AddOnMeta[ 'quantity' ]; + } + | undefined + | null = getSelectedStorageAddOn( storageAddOns, defaultStorageOptionSlug || '' ); + + // If the default storage add-on is not available, create a new object with the default storage option slug + if ( ! defaultStorageAddOnMeta && defaultStorageOptionSlug ) { + defaultStorageAddOnMeta = { addOnSlug: defaultStorageOptionSlug, prices: null, quantity: 0 }; + } - const selectControlOptions = [ defaultStorageItem ].concat( - availableStorageAddOns?.map( ( addOn ) => { - const addOnStorage = addOn.quantity ?? 0; + return [ defaultStorageAddOnMeta, ...availableStorageAddOns ]?.map( ( addOn ) => { + const addOnStorage = addOn?.quantity ?? 0; return { - key: addOn.addOnSlug, + key: addOn?.addOnSlug || '', name: ( ) as unknown as string, }; - } ) - ); + } ); + }, [ availableStorageAddOns, defaultStorageOptionSlug, planStorage, storageAddOns ] ); const selectedStorageAddOn = getSelectedStorageAddOn( storageAddOns, diff --git a/test/e2e/specs/media/settings__media.ts b/test/e2e/specs/media/settings__media.ts index a942270a2c83e0..bf298adf4b6386 100644 --- a/test/e2e/specs/media/settings__media.ts +++ b/test/e2e/specs/media/settings__media.ts @@ -6,7 +6,6 @@ import { DataHelper, TestAccount, - SidebarComponent, envVariables, getTestAccountByFeature, envToFeatureKey, @@ -44,6 +43,16 @@ describe( DataHelper.createSuiteTitle( 'Jetpack Settings: Media' ), function () } else { await testAccount.authenticate( page ); } + + // Atomic tests sites might have local users, so the Jetpack SSO login will + // show up when visiting the Jetpack dashboard directly. We can bypass it if + // we simulate a redirect from Calypso to WP Admin with a hardcoded referer. + // @see https://github.com/Automattic/jetpack/blob/12b3b9a4771169398d4e1982573aaec820babc17/projects/plugins/wpcomsh/wpcomsh.php#L230-L254 + const siteUrl = testAccount.getSiteURL( { protocol: true } ); + await page.goto( `${ siteUrl }wp-admin/`, { + timeout: 15 * 1000, + referer: 'https://wordpress.com/', + } ); } ); if ( envVariables.JETPACK_TARGET !== 'remote-site' ) { @@ -51,14 +60,7 @@ describe( DataHelper.createSuiteTitle( 'Jetpack Settings: Media' ), function () let wpAdminMediaSettingsPage: WpAdminMediaSettingsPage; it( 'Navigate to Settings > Media', async function () { - // eCommerce plan loads WP-Admin for home dashboard, - // so instead navigate straight to the Media page. - if ( testAccount.accountName === 'jetpackAtomicEcommPlanUser' ) { - await page.goto( `${ testAccount.getSiteURL() }wp-admin/options-media.php` ); - } else { - const sidebarComponent = new SidebarComponent( page ); - await sidebarComponent.navigate( 'Settings', 'Media' ); - } + await page.goto( `${ testAccount.getSiteURL() }wp-admin/options-media.php` ); wpAdminMediaSettingsPage = new WpAdminMediaSettingsPage( page ); } ); diff --git a/test/e2e/specs/onboarding/onboarding__write.ts b/test/e2e/specs/onboarding/onboarding__write.ts index 9b792433b06d78..5b55d0f8e91865 100644 --- a/test/e2e/specs/onboarding/onboarding__write.ts +++ b/test/e2e/specs/onboarding/onboarding__write.ts @@ -264,6 +264,7 @@ describe( DataHelper.createSuiteTitle( 'Goals-First Onboarding: Write Focus' ), it( 'Editor loads', async function () { editorPage = new EditorPage( page ); await editorPage.waitUntilLoaded(); + await editorPage.closeWelcomeGuideIfNeeded(); await page.waitForURL( new RegExp( newSiteDetails.blog_details.site_slug ) ); } ); diff --git a/test/e2e/specs/onboarding/signup__start-writing-tailored.ts b/test/e2e/specs/onboarding/signup__start-writing-tailored.ts index 4fbdd03c3869de..22a56de8372580 100644 --- a/test/e2e/specs/onboarding/signup__start-writing-tailored.ts +++ b/test/e2e/specs/onboarding/signup__start-writing-tailored.ts @@ -40,6 +40,7 @@ describe( 'Signup: Tailored Start Writing Flow', () => { it( 'Publish first post', async function () { const editorPage = new EditorPage( page ); await editorPage.waitUntilLoaded(); + await editorPage.closeWelcomeGuideIfNeeded(); await editorPage.enterTitle( 'my first post title' ); await editorPage.publish(); await page.getByText( "Your blog's almost ready!" ).waitFor(); diff --git a/test/e2e/specs/onboarding/signup__woo-email.ts b/test/e2e/specs/onboarding/signup__woo-email.ts index 6bd978f2b7e631..048241f263ce5f 100644 --- a/test/e2e/specs/onboarding/signup__woo-email.ts +++ b/test/e2e/specs/onboarding/signup__woo-email.ts @@ -55,7 +55,7 @@ describe( } ); it( 'Activate account', async function () { - await page.goto( activationLink, { waitUntil: 'networkidle' } ); + await page.goto( activationLink, { waitUntil: 'networkidle', timeout: 25000 } ); } ); } );