From aeccbd35917d16ac0b4955bc92beceddcba0a6f3 Mon Sep 17 00:00:00 2001 From: Tiago Ilieve Date: Wed, 5 Feb 2025 20:13:47 -0300 Subject: [PATCH 01/38] Stats: prompt users to update the Jetpack plugin (#99278) * Stats: prompt users to update the Jetpack plugin * Use a different icon for the Jetpack update card * Refactor module overlay * Updated enabled filter on useLocationViewsQuery * Explain when the legacy countries endpoint is used --- .../stats-locations/stats-locations.tsx | 54 ++++++++++++++----- client/my-sites/stats/site.jsx | 4 +- .../stats-card-update-jetpack-version.tsx | 1 + .../stats-card-upsell-overlay.tsx | 4 +- 4 files changed, 46 insertions(+), 17 deletions(-) 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 d8d9669eab3cb..a97c59116131f 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'; @@ -98,15 +101,27 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary const geoMode = GEO_MODES[ selectedOption ]; const title = optionLabels[ selectedOption ]?.selectLabel; + 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', } ); @@ -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,14 +324,7 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary : undefined } onShowMoreClick={ onShowMoreClick } - overlay={ - shouldGate && ( - - ) - } + overlay={ moduleOverlay } /> ) } diff --git a/client/my-sites/stats/site.jsx b/client/my-sites/stats/site.jsx index e4be8976926cc..79b3ba896956b 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 f3d9f2bb327aa..ced57b6653e4a 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 ? ( From 9a54ac5374cc12e4f574554ba325cb76f5c06d9b Mon Sep 17 00:00:00 2001 From: Andrii Lysenko <60262784+andrii-lysenko@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:35:14 -0800 Subject: [PATCH 02/38] A4A: add forms for new signup flow (#99186) --- .../a8c-for-agencies/sections/signup/index.ts | 5 +- .../multi-step-form/blueprint-form/index.tsx | 180 ++++++++++++++++++ .../choice-blueprint/index.tsx | 40 ++++ .../choice-blueprint/style.scss | 16 ++ .../multi-step-form/contact-form/index.tsx | 162 ++++++++++++++++ .../multi-step-form/contact-form/style.scss | 99 ++++++++++ .../finish-signup-survey/index.tsx | 28 +++ .../components/multi-step-form/index.tsx | 62 ++++++ .../multi-step-form/personalization/index.tsx | 170 +++++++++++++++++ .../personalization/style.scss | 91 +++++++++ .../components/multi-step-form/style.scss | 24 +++ .../components/signup-wrapper/index.tsx | 1 + .../signup-wrapper/sidebar-image.tsx | 26 ++- .../components/signup-wrapper/style.scss | 114 +++++++---- .../components/step-progress/index.tsx | 38 ++++ .../components/step-progress/style.scss | 73 +++++++ .../sections/signup/signup-v2/index.tsx | 11 +- .../sections/signup/signup-v2/types.ts | 1 + config/a8c-for-agencies-development.json | 3 +- config/a8c-for-agencies-horizon.json | 3 +- 20 files changed, 1090 insertions(+), 57 deletions(-) create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/choice-blueprint/style.scss create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/contact-form/style.scss create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/finish-signup-survey/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/personalization/style.scss create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/style.scss create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/index.tsx create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/components/step-progress/style.scss create mode 100644 client/a8c-for-agencies/sections/signup/signup-v2/types.ts diff --git a/client/a8c-for-agencies/sections/signup/index.ts b/client/a8c-for-agencies/sections/signup/index.ts index 80777eb476836..a52da177575e1 100644 --- a/client/a8c-for-agencies/sections/signup/index.ts +++ b/client/a8c-for-agencies/sections/signup/index.ts @@ -1,3 +1,4 @@ +import { isEnabled } from '@automattic/calypso-config'; import page from '@automattic/calypso-router'; import { makeLayout, render as clientRender } from 'calypso/controller'; import * as controller from './controller'; @@ -5,6 +6,8 @@ import * as controller from './controller'; export default function () { page( '/signup', controller.signUpContext, makeLayout, clientRender ); page( '/signup/finish', controller.finishSignUpContext, makeLayout, clientRender ); - page( '/signup/wc-asia', controller.wcAsiaSignupContext, makeLayout, clientRender ); + if ( isEnabled( 'a4a-wc-asia-signup-enabled' ) ) { + page( '/signup/wc-asia', controller.wcAsiaSignupContext, makeLayout, clientRender ); + } page( '/signup/oauth/token', controller.tokenRedirect, makeLayout, clientRender ); } diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx new file mode 100644 index 0000000000000..449c06295d739 --- /dev/null +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx @@ -0,0 +1,180 @@ +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 FormRadio from 'calypso/components/forms/form-radio'; +import FormTextarea from 'calypso/components/forms/form-textarea'; +import { preventWidows } from 'calypso/lib/formatting'; + +type Props = { + onContinue: () => void; +}; + +type FormData = { + topGoal: string; + mainGoal2025: string; + workModel: string; + specificApproach: string; +}; + +const BlueprintForm: React.FC< Props > = ( { onContinue } ) => { + const translate = useTranslate(); + const [ formData, setFormData ] = useState< FormData >( { + topGoal: '', + mainGoal2025: '', + workModel: '', + specificApproach: '', + } ); + + const handleSubmit = ( e: React.FormEvent ) => { + e.preventDefault(); + onContinue(); + }; + + return ( +
+
+ +
+ setFormData( { ...formData, topGoal: 'cutting_edge_tech' } ) } + /> + setFormData( { ...formData, topGoal: 'comprehensive_support' } ) } + /> + setFormData( { ...formData, topGoal: 'lead_generation' } ) } + /> + setFormData( { ...formData, topGoal: 'training_education' } ) } + /> + setFormData( { ...formData, topGoal: 'exclusive_discounts' } ) } + /> +
+
+ + +
+ setFormData( { ...formData, mainGoal2025: 'scaling_agency' } ) } + /> + + setFormData( { ...formData, mainGoal2025: 'diversifying_services' } ) + } + /> + setFormData( { ...formData, mainGoal2025: 'increasing_clients' } ) } + /> + setFormData( { ...formData, mainGoal2025: 'expanding_markets' } ) } + /> + setFormData( { ...formData, mainGoal2025: 'strengthening_brand' } ) } + /> +
+
+ + +
+ setFormData( { ...formData, workModel: 'refer_customers' } ) } + /> + setFormData( { ...formData, workModel: 'purchase_resell' } ) } + /> + setFormData( { ...formData, workModel: 'combination' } ) } + /> + setFormData( { ...formData, workModel: 'other' } ) } + /> + { formData.workModel === 'other' && ( + { + // TODO: form data + return; + } } + placeholder={ translate( 'Add your explanation' ) } + /> + ) } +
+
+ + + ) => + setFormData( { ...formData, specificApproach: e.target.value } ) + } + placeholder={ translate( 'Add your approach' ) } + /> + + +
+ +
+
+
+ ); +}; + +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 0000000000000..af652a7f21b88 --- /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 0000000000000..cf82a9f7a325e --- /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 0000000000000..a3571b9612a80 --- /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 0000000000000..3062539f37da9 --- /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 0000000000000..2cc753a86ee98 --- /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 0000000000000..a343136197764 --- /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 0000000000000..91a11bd83757a --- /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 0000000000000..cdabca47d95dd --- /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 0000000000000..cd86ad82f1728 --- /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 0ba9351a6a768..54431c8cddb7b 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 452008c94e19a..79ffb88a28de8 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 0e99465a17e48..6cedab317d75b 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 0000000000000..ab58f3add17c2 --- /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 0000000000000..0352975c3bd11 --- /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 ab58a9fb57bb2..963a85b6ab541 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 0000000000000..64c508fd0cbd2 --- /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/config/a8c-for-agencies-development.json b/config/a8c-for-agencies-development.json index 310e3d2b83290..cd17d7260f039 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 1ce052f46e5d0..186080d3ca558 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": { From b2c3c92d3f5a63d6baeb3b37f60c29c2d42ad3e0 Mon Sep 17 00:00:00 2001 From: Louis Laugesen Date: Thu, 6 Feb 2025 10:54:03 +1100 Subject: [PATCH 03/38] Include query params in start-site-transfer redirect (#99383) --- client/my-sites/site-settings/settings-controller.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/my-sites/site-settings/settings-controller.js b/client/my-sites/site-settings/settings-controller.js index ea4544c59d893..bb93c0ac3a33f 100644 --- a/client/my-sites/site-settings/settings-controller.js +++ b/client/my-sites/site-settings/settings-controller.js @@ -54,7 +54,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(); From bb95cedd5d980aa2ff8e4ddd4875747c6880200d Mon Sep 17 00:00:00 2001 From: Aneesh Devasthale Date: Wed, 5 Feb 2025 16:24:42 -0800 Subject: [PATCH 04/38] Plans Grid: fix default state of storage dropdown when an addon is purchased (#99310) * Plans Grid: fix default state of storage dropdown when an addon is purchased * StorageDropdown: enhance type definitions for default storage add-on meta --- .../storage/components/storage-dropdown.tsx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) 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 84c6167a86429..d7f44d7450c19 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, From 2ce287746bb5935ce0860054a5401aec3643a08d Mon Sep 17 00:00:00 2001 From: "okmttdhr, tada" Date: Thu, 6 Feb 2025 11:25:35 +0900 Subject: [PATCH 05/38] Add settings page redirects to wp-admin for duplicate view experiment (#99321) Co-authored-by: Louis Laugesen --- client/my-sites/site-settings/settings-controller.js | 3 ++- client/my-sites/site-settings/settings-discussion/index.js | 3 ++- client/my-sites/site-settings/settings-reading/index.ts | 3 ++- client/my-sites/site-settings/settings-writing/index.js | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/my-sites/site-settings/settings-controller.js b/client/my-sites/site-settings/settings-controller.js index bb93c0ac3a33f..e6557ac9cd462 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'; @@ -97,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 d35d32a77c1a9..50db7f3a47dc3 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-reading/index.ts b/client/my-sites/site-settings/settings-reading/index.ts index b616fa30d668f..cff0139eb199a 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 3c0c55602df13..46699387ca4dc 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, From 9512f79beabd7dabe255c1068c3f00138a26671e Mon Sep 17 00:00:00 2001 From: Yashwin Poojary Date: Thu, 6 Feb 2025 09:07:20 +0530 Subject: [PATCH 06/38] A4A > Agency Tier: Allow agencies to download the agency tier badges (#99329) --- .../agency-tier/download-badges/download-link.tsx | 4 ++-- .../agency-tier/primary/agency-tier-overview/index.tsx | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) 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 d664289779cd5..fb6fad6957c4f 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 c69753225353a..fe506e27387b6 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 ( From fb86ffd2221d4468222ad3acb545b48905af3722 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Thu, 6 Feb 2025 10:54:02 +0700 Subject: [PATCH 07/38] E2E: Remove invalid test after Settings > Media untangling (#99393) --- test/e2e/specs/media/settings__media.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/e2e/specs/media/settings__media.ts b/test/e2e/specs/media/settings__media.ts index a942270a2c83e..1f9d3e5394b30 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, @@ -51,14 +50,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 ); } ); From 6f5520990f79eebfee7da4ccc6416d026c7da06a Mon Sep 17 00:00:00 2001 From: Candy Tsai Date: Thu, 6 Feb 2025 14:16:16 +0800 Subject: [PATCH 08/38] Fix design picker active theme styles (#99333) --- packages/design-picker/src/components/style.scss | 6 ++++-- packages/design-picker/src/components/theme-card/style.scss | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/design-picker/src/components/style.scss b/packages/design-picker/src/components/style.scss index 8de01189b0567..b21ec375e2453 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 1696381fa79a1..12add3144ad99 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); From 5e739f09c5ae985918c65f0c09205d00b0a98ec4 Mon Sep 17 00:00:00 2001 From: Candy Tsai Date: Thu, 6 Feb 2025 14:46:32 +0800 Subject: [PATCH 09/38] Hide active badge for design picker in /setup/site-setup flow (#99334) --- .../design-setup/unified-design-picker.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 95d334c6525fe..c6ed5b8b73827 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,7 +22,12 @@ 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'; @@ -943,7 +948,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 } /> From c7b972193567fded890ae2e3db9d53a76e0e0e7f Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 6 Feb 2025 08:25:11 +0100 Subject: [PATCH 10/38] Server: re-read assets.json file when modified (#99350) * Server: re-read assets.json file when modified * Use file descriptors * Run the check only once at a time --- client/server/middleware/assets.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/client/server/middleware/assets.js b/client/server/middleware/assets.js index 986dc03e475fe..983aeb61fad13 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(); From 175e02b7f9519aa095d2eeaeb9f78e468013ed1c Mon Sep 17 00:00:00 2001 From: Miguel Torres <1233880+mmtr@users.noreply.github.com> Date: Thu, 6 Feb 2025 09:11:48 +0100 Subject: [PATCH 11/38] RDV: Update overriding preference (#99361) --- client/controller/index.web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/controller/index.web.js b/client/controller/index.web.js index 0c0d1232ff578..0b939c790e90b 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 ) { From 4d5beb47fd4ac64036405bb438ece6fdf0e4d461 Mon Sep 17 00:00:00 2001 From: Eoin Gallagher Date: Thu, 6 Feb 2025 08:22:31 +0000 Subject: [PATCH 12/38] Newsletter: Add hide category selection modal setting (#99165) * Newsletter: Add hide category selection modal setting * add hide modal setting * add feature flag check * update setting name --- .../settings-newsletter/main.tsx | 4 ++ ...ewsletter-categories-hide-modal-toggle.tsx | 40 +++++++++++++++++++ .../newsletter-categories-section.tsx | 23 +++++++++++ .../newsletter-categories-section/style.scss | 6 +++ config/wpcalypso.json | 2 +- 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 client/my-sites/site-settings/settings-newsletter/newsletter-categories-section/newsletter-categories-hide-modal-toggle.tsx diff --git a/client/my-sites/site-settings/settings-newsletter/main.tsx b/client/my-sites/site-settings/settings-newsletter/main.tsx index 9a1d06921d7e9..9f591bbabad8e 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 0000000000000..7eda42187f48b --- /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 4ce017bc27b79..080fcc86989c1 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 66ae08fe66fd4..ae8d62b55561a 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/config/wpcalypso.json b/config/wpcalypso.json index 64149c9d79923..92d4a4afff6c5 100644 --- a/config/wpcalypso.json +++ b/config/wpcalypso.json @@ -106,7 +106,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, From fab519b040a92ca989a08de11c85d5778ee39dd7 Mon Sep 17 00:00:00 2001 From: Roberto Aranda Date: Thu, 6 Feb 2025 10:11:12 +0100 Subject: [PATCH 13/38] Bulk Plugin Management: Avoid CSS leak when navigating to plugin details (#99224) * Bulk Plugin Management: Avoid CSS leak when navigating to plugin details * Use all sites instead of the selected one for listing the plugins * Clear selected site when navigating to plugins management screen * Add the noSite middleware to the plugins/manage/sites/:slug route --- client/my-sites/plugins/index.web.js | 4 +++- client/my-sites/plugins/plugins-dashboard/index.tsx | 12 +++++------- client/state/global-sidebar/selectors.ts | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/client/my-sites/plugins/index.web.js b/client/my-sites/plugins/index.web.js index 1c06ce5fa3cdb..06ef9b08ba942 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 8479a4cb63075..2e9a642c1be91 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/state/global-sidebar/selectors.ts b/client/state/global-sidebar/selectors.ts index 5168558e5cecd..dfeecad2a0ae8 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[] ) { From 942af051c9561359e491b6274159892f548c3305 Mon Sep 17 00:00:00 2001 From: Anthony Grullon Date: Thu, 6 Feb 2025 16:47:14 +0700 Subject: [PATCH 14/38] Help Center: update message component styles for improved layout and spacing (#99402) * style: update message component styles for improved layout and spacing * style: remove margin from list elements in message component --- .../src/components/message/style.scss | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/odie-client/src/components/message/style.scss b/packages/odie-client/src/components/message/style.scss index 4fc164c7e151f..ece9a721a63ae 100644 --- a/packages/odie-client/src/components/message/style.scss +++ b/packages/odie-client/src/components/message/style.scss @@ -64,8 +64,9 @@ $blueberry-color: #3858e9; ol, ul { - margin: 0 0 0 1em; - padding: 16px; + margin: 0; + padding-left: 20px; + padding-right: 20px; font-size: 0.875rem; font-style: normal; @@ -96,6 +97,25 @@ $blueberry-color: #3858e9; /* stylelint-disable-next-line scales/radii */ border-radius: 8px 8px 8px 0; max-width: 256px; + + p { + font-size: 0.875rem; + margin-bottom: 0; + margin-top: 0; + } + + > *: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; } } From 91f80a439edc51c275aef2da7d6c67d9a4e9811f Mon Sep 17 00:00:00 2001 From: James Kenneth Guidaven Date: Thu, 6 Feb 2025 17:54:57 +0800 Subject: [PATCH 15/38] A4A: Convert Bundle price selector to use core component. (#99094) --- .../bundle-price-selector/index.tsx | 78 ++++++------------- 1 file changed, 22 insertions(+), 56 deletions(-) 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 05ef1aad3fc4a..7f8529bde7ad9 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 ) }`, + } ) ), + ] } + /> ); } From 04212078b221ebf48622058920ac66ce2e7d6f83 Mon Sep 17 00:00:00 2001 From: James Kenneth Guidaven Date: Thu, 6 Feb 2025 17:55:26 +0800 Subject: [PATCH 16/38] A4A: Add few improvements on the new product marketplace. (#99091) * Fix visual imbalance on the Referral toggle. * Move 'Payments' category as 3rd item in the menu. * Change featured products. * Make lightbox details expanded by default on mobile view. --- .../marketplace/hooks/use-product-and-plans.tsx | 13 +++++++------ .../marketplace/hosting-overview-v3/style.scss | 2 +- .../marketplace/products-overview-v2/style.scss | 2 +- .../hooks/use-product-filter-options.tsx | 10 +++++----- .../jetpack/jetpack-product-info/section.tsx | 1 + 5 files changed, 15 insertions(+), 13 deletions(-) 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 bc8d32c546b7f..cb93e84e049a5 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 95d997fb2f3c7..726d3bb44c3a6 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/style.scss b/client/a8c-for-agencies/sections/marketplace/products-overview-v2/style.scss index 380754cd3c5da..3403c51342bf1 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 f2e43b8bfa7e8..1362d4503cfdd 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/components/jetpack/jetpack-product-info/section.tsx b/client/components/jetpack/jetpack-product-info/section.tsx index d889037e2f65c..bde94a9f19cb1 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 }
From 68a2f75dbe93f62d51349df2c587e11e61313d9e Mon Sep 17 00:00:00 2001 From: Miroslav Mitev Date: Thu, 6 Feb 2025 12:18:49 +0200 Subject: [PATCH 17/38] Domain management: Update domain header font-size (#99356) --- .../components/domain-header/index.tsx | 1 + .../components/domain-header/style.scss | 11 +++++++++++ 2 files changed, 12 insertions(+) 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 530aac49f33a2..02611863df9c4 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 1257060fc4d11..5b03f0a2eee3b 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; From 18d2df267094cb5a855795cb2fe1bad2db49735e Mon Sep 17 00:00:00 2001 From: Christos Date: Thu, 6 Feb 2025 12:20:58 +0200 Subject: [PATCH 18/38] i18n: Update several cases of ShortenedNumber component with numberFormat (#99272) --- apps/odyssey-stats/src/widget/modules.tsx | 13 +++++-- .../site-expanded-content/insights-stats.tsx | 22 +++++++++-- .../site-status-content/site-stats-column.tsx | 11 +++++- .../all-time-highlights-section/index.tsx | 21 ++++++----- .../stats/stats-email-top-row/top-card.jsx | 11 ++++-- .../subscriber-stats-card.tsx | 12 +++++- .../horizontal-bar-grid-item.tsx | 13 ++++++- .../components/src/post-stats-card/index.tsx | 37 ++++++++++++++++--- 8 files changed, 110 insertions(+), 30 deletions(-) diff --git a/apps/odyssey-stats/src/widget/modules.tsx b/apps/odyssey-stats/src/widget/modules.tsx index 961850df5d54a..c98131b7fd8d4 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/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 03478eaa893da..4bae6bdc08392 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 25e934fdbca55..46f3b3163a559 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/my-sites/stats/sections/all-time-highlights-section/index.tsx b/client/my-sites/stats/sections/all-time-highlights-section/index.tsx index 10bd99635aa72..4afc29d01afe2 100644 --- a/client/my-sites/stats/sections/all-time-highlights-section/index.tsx +++ b/client/my-sites/stats/sections/all-time-highlights-section/index.tsx @@ -1,10 +1,4 @@ -import { - Card, - ComponentSwapper, - formattedNumber, - ShortenedNumber, - DotPager, -} from '@automattic/components'; +import { Card, ComponentSwapper, formattedNumber, DotPager } from '@automattic/components'; import { formatPercentage, percentCalculator, @@ -12,7 +6,7 @@ import { import { eye } from '@automattic/components/src/icons'; import { Icon, people, postContent, starEmpty, commentContent } from '@wordpress/icons'; import clsx from 'clsx'; -import { useTranslate } from 'i18n-calypso'; +import { useTranslate, numberFormat } from 'i18n-calypso'; import React, { useMemo } from 'react'; import QueryPosts from 'calypso/components/data/query-posts'; import QuerySiteStats from 'calypso/components/data/query-site-stats'; @@ -154,7 +148,16 @@ export default function AllTimeHighlightsSection( { { id: 'views', header: translate( 'Views' ), - content: , + content: ( + + { numberFormat( viewsBestDayTotal, { + numberFormatOptions: { + notation: 'compact', + maximumFractionDigits: 1, + }, + } ) } + + ), footer: translate( '%(percent)s of views', { args: { percent: formatPercentage( bestViewsEverPercent, true ) }, context: 'Stats: Percentage of views', 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 72b4b75248821..bb0c0a722a058 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/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx b/client/my-sites/subscribers/components/subscriber-stats-card/subscriber-stats-card.tsx index b98bb4b64d330..18bdd33cefec7 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/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx b/packages/components/src/horizontal-bar-list/horizontal-bar-grid-item.tsx index 2a8e1362eb779..52043f220abcc 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 50a630a486ae5..9e5a3c88ae3a9 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, + }, + } ) + : '-' } +
From 84dbe1fb38547efaf4cb998e89e64b4ceb7d2eb6 Mon Sep 17 00:00:00 2001 From: Ilyas Foo Date: Thu, 6 Feb 2025 18:24:57 +0800 Subject: [PATCH 19/38] Fix promise and add timeout for more resilience (#99403) --- .../calypso-e2e/src/lib/pages/signup/user-signup-page.ts | 9 ++++++--- test/e2e/specs/onboarding/signup__woo-email.ts | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) 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 31b8b19c4fb19..ae72f0aa21e78 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/test/e2e/specs/onboarding/signup__woo-email.ts b/test/e2e/specs/onboarding/signup__woo-email.ts index 6bd978f2b7e63..048241f263ce5 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 } ); } ); } ); From 133adfd45c1e6acf0902d84f4b68a0d8ebf42f03 Mon Sep 17 00:00:00 2001 From: Emanuele Buccelli Date: Thu, 6 Feb 2025 11:34:56 +0100 Subject: [PATCH 20/38] Help Center: Remove webinars (#99404) --- .../components/help-center-more-resources.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) 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 b07f64fb850bc..8ac55e46cc95c 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 = () => {
-
  • - -
  • Date: Thu, 6 Feb 2025 18:36:33 +0800 Subject: [PATCH 21/38] A4A: Load schedule call link from company record. (#98351) * Make Schedule call link dynamic based from Company record. * Fix disabled button styling getting override. * Show busy state on the CTA button. --- .../agencies/use-fetch-schedule-call-link.ts | 19 ++++++++++++++++++ .../sidebar/growth-accelerator/index.tsx | 20 +++++++++++++++---- client/a8c-for-agencies/style.scss | 2 +- 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 client/a8c-for-agencies/data/agencies/use-fetch-schedule-call-link.ts 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 0000000000000..6ba26daf51ef6 --- /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/overview/sidebar/growth-accelerator/index.tsx b/client/a8c-for-agencies/sections/overview/sidebar/growth-accelerator/index.tsx index cafc9ca73605b..10043472061ce 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 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 0ec1fc70bc8f9..0000000000000 --- 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 9c86bad1f6aaf..0000000000000 --- 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 1ce2b8f305108..0000000000000 --- 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 61fe8d45e3895..0000000000000 --- 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 2cdb076c5e578..0000000000000 --- 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 1fb90d976d992..0000000000000 --- 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 ebf26fdcb96b9..0000000000000 --- 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 78856a5bfa6a5..0000000000000 --- 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 25654cc9d9158..0000000000000 --- 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 7828eb1592947..0000000000000 --- 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 cca5764046f64..0000000000000 --- 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 0953aec3905c8..0000000000000 --- 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 56e20858e1d33..0000000000000 --- 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 eb21c2001f50b..0000000000000 --- 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 36548a6e04b5c..0000000000000 --- 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 03c4a563649c8..0000000000000 --- 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/config/development.json b/config/development.json index 46554005f5a28..1b579894b964c 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 c97c4ccf97dea..0059065f4e036 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 46e08bf8a589b..e7ce22247c1dd 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 8157daddaa51d..81b50d0fc80d0 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 9f0a5079fd12a..5ea47c345e404 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 92d4a4afff6c5..154af23dbaf9d 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, diff --git a/packages/calypso-products/src/constants/jetpack.ts b/packages/calypso-products/src/constants/jetpack.ts index 830c788a9703a..2ba835a85c18c 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 From d13b1598a4576101f13ede914146517680ce2c56 Mon Sep 17 00:00:00 2001 From: Emanuele Buccelli Date: Thu, 6 Feb 2025 12:34:01 +0100 Subject: [PATCH 24/38] Stepper: Unify loading states (#99158) * First iteration on the unified loading bar * Unused progress in launchpad * Change checkout margin and color * Fix StepperLoader * Handle wooexpress * Fix types * Remove old classnames and address wooexpress * Handle boot fallback missing padding * Fix bigsky, launchpad, tailoredcheckout * Unused CSS * Fix wooexpress * Remove unused classname * Fix site launch * Not show bar if error on bigsky * Converte progress to 0-100 * Fix tailored logic * Default to undefined for progress * Missing useEffect * Fit unit test --- .../components/stepper-loader/index.tsx | 25 +++++++-- .../components/stepper-loader/style.scss | 42 ++++++++++++++- .../declarative-flow/internals/global.scss | 30 ++++++----- .../declarative-flow/internals/index.tsx | 6 ++- .../assign-trial-plan/index.tsx | 28 +++------- .../assign-trial-plan/styles.scss | 2 - .../automated-copy-site/index.tsx | 10 ++-- .../bundle-install-plugins/index.tsx | 4 +- .../bundle-transfer/index.tsx | 10 ++-- .../steps-repository/create-site/index.tsx | 18 +++---- .../steps-repository/create-site/styles.scss | 1 - .../steps-repository/launch-big-sky/index.tsx | 23 ++++----- .../steps-repository/launchpad/style.scss | 6 ++- .../processing-step/README.md | 12 ++--- .../processing-step/index.tsx | 38 +++----------- .../processing-step/style.scss | 21 ++------ .../index.tsx | 51 +++++++------------ .../style.scss | 14 +---- .../steps-repository/site-launch/index.tsx | 13 ++--- .../site-migration-plugin-install/index.tsx | 2 +- .../test/index.tsx | 6 +-- .../checkout-thank-you/pending/style.scss | 24 +++++---- .../transfer/install-plugins.tsx | 4 +- .../transfer/transfer-site.tsx | 12 ++--- packages/data-stores/src/onboard/reducer.ts | 4 +- 25 files changed, 187 insertions(+), 219 deletions(-) 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 4ef75cfff684d..cfe067f78db28 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 c1832c29161b2..aa3cbb65e801d 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 e0bd332ce53ac..17b57b5bb2227 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 06fc89767b1b4..ff9dd4816bd00 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'; @@ -156,6 +157,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; @@ -230,7 +234,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 c1065037a9626..ff5d53dc12e0e 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 d9412b2a3ac43..0a52113e0aa83 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 f47d8442b2c83..c8a922e4e9a76 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 15dd78074f5cc..5ff83b1bd21fa 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 fc83bfc2eeb63..e3841ffb41777 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 54edb281fa4e3..f576635aa5dbc 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 7b6a8be559c01..8a6e5e3007b1c 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/launch-big-sky/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/launch-big-sky/index.tsx index 13a039c037dbd..345c209a4b86c 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 4b2f949a4da42..e44b040615925 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 f3dbfc3a4873f..c83d22fad302d 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 1dac0a370d985..0849bd1c3ebda 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 ee49502aa515f..a8c6282f7995f 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 5b07408a2822f..225ada5130732 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 7ea27616c8d37..f29592debc706 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 71d4ab22bc24d..6fbc3e791a2dd 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 0763c115c8c3c..967fbc6faaf28 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 6b9e1054fbcca..fe30144a4cdaf 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/my-sites/checkout/checkout-thank-you/pending/style.scss b/client/my-sites/checkout/checkout-thank-you/pending/style.scss index dc8f7c3ca2a52..aec2f9bea0dc8 100644 --- a/client/my-sites/checkout/checkout-thank-you/pending/style.scss +++ b/client/my-sites/checkout/checkout-thank-you/pending/style.scss @@ -1,3 +1,5 @@ +@import "@wordpress/base-styles/mixins"; + .pending-content__wrapper { padding: 1em; text-align: center; @@ -6,27 +8,27 @@ .pending-content__title { @extend .wp-brand-font; - /* stylelint-disable-next-line scales/font-sizes */ - font-size: 1.625rem; line-height: 40px; text-align: center; vertical-align: middle; margin: 0; -} + font-size: $font-title-medium; +} .pending-content__progress-bar { - margin: 24px auto 0 auto; + --wp-components-color-foreground: #3858e9; + margin: 46px auto 0 auto; } .pending-content__info-text-container { display: inline-block; - font-size: 1rem; - text-align: left; - border: 1px solid var( --studio-gray-5 ); - padding: 1rem; + font-size: 1rem; + text-align: left; + border: 1px solid var( --studio-gray-5 ); + padding: 1rem; @extend .wp-brand-font; - border-radius: 4px; - background-color: var( --studio-gray-0 ); + border-radius: 4px; + background-color: var( --studio-gray-0 ); .pending-content__info-text { > span { @@ -35,4 +37,4 @@ color: var( --studio-gray-60 ); } } -} \ No newline at end of file +} diff --git a/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx b/client/signup/steps/woocommerce-install/transfer/install-plugins.tsx index 6f531f9a0ee21..6d3f04f4b2b18 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 596d87f64e28e..56c3b9fcb9b1f 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/packages/data-stores/src/onboard/reducer.ts b/packages/data-stores/src/onboard/reducer.ts index 72f8c29e216c1..32a2da9673a7a 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; }; From e36c5aab91e46b7d2f0adcf7eaf6fbe6cb036941 Mon Sep 17 00:00:00 2001 From: Miroslav Mitev Date: Thu, 6 Feb 2025 13:34:58 +0200 Subject: [PATCH 25/38] Domain management: Fix redirect context on mailbox purchase (#99263) --- client/my-sites/email/add-mailboxes/index.tsx | 24 +++++------------ .../get-on-submit-new-mailboxes-handler.ts | 12 ++++++++- .../provider-cards/google-workspace-card.tsx | 3 +++ .../professional-email-card.tsx | 3 +++ client/my-sites/email/paths.ts | 24 +++++++++++++++++ client/my-sites/email/test/paths.ts | 26 +++++++++++++++++++ 6 files changed, 74 insertions(+), 18 deletions(-) diff --git a/client/my-sites/email/add-mailboxes/index.tsx b/client/my-sites/email/add-mailboxes/index.tsx index 0215c9035981a..ea8cd5a54c5ef 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-providers-comparison/stacked/provider-cards/get-on-submit-new-mailboxes-handler.ts b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/get-on-submit-new-mailboxes-handler.ts index da9ca4f63770f..8d1b54ff84774 100644 --- a/client/my-sites/email/email-providers-comparison/stacked/provider-cards/get-on-submit-new-mailboxes-handler.ts +++ b/client/my-sites/email/email-providers-comparison/stacked/provider-cards/get-on-submit-new-mailboxes-handler.ts @@ -12,6 +12,7 @@ import { } from 'calypso/my-sites/email/form/mailboxes/components/utilities/get-email-product-properties'; import { MailboxOperations } from 'calypso/my-sites/email/form/mailboxes/components/utilities/mailbox-operations'; import { EmailProvider } from 'calypso/my-sites/email/form/mailboxes/types'; +import { getEmailCheckoutPath } from 'calypso/my-sites/email/paths'; import { errorNotice } from 'calypso/state/notices/actions'; import { ProductListItem } from 'calypso/state/products-list/selectors/get-products-list'; @@ -26,6 +27,7 @@ export type GetOnSubmitNewMailboxesHandlerProps = { shoppingCartManager: ShoppingCartManagerActions; siteSlug: string; source: string; + currentRoute: string; }; const getEmailProductPropertiesForUpsell = ( @@ -51,6 +53,7 @@ const getOnSubmitNewMailboxesHandler = shoppingCartManager, siteSlug, source, + currentRoute, }: GetOnSubmitNewMailboxesHandlerProps ) => 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 a08ea5a8e1088..5066cc7a5736a 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 56d15c49953da..47bbd16a392d4 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 cb7d7dc2bb3d9..e472a8b85ebd3 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 f0e5bb941973f..c858539f49d97 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 ], From 3c3eb3610875b4283c5a36cca7884610b81f53fc Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 6 Feb 2025 12:58:21 +0100 Subject: [PATCH 26/38] Fix issues when conversation can't be found (#99337) --- .../src/data/use-get-zendesk-conversation.ts | 16 +---- .../src/hooks/use-get-combined-chat.ts | 59 ++++++++++++------- 2 files changed, 40 insertions(+), 35 deletions(-) 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 da099334d4af9..385b376f4c95c 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 7ae08496a38f9..4fcba1979188a 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 ) => ( { From 27284c6b8157bd2fd0982753cb438c2caf5be4da Mon Sep 17 00:00:00 2001 From: Imran Hossain Date: Thu, 6 Feb 2025 19:23:10 +0600 Subject: [PATCH 27/38] Align all domain overview pages to center except upsells (#99377) --- .../domain-management/domain-overview-pane/style.scss | 11 +++++++++++ client/my-sites/email/email-management/email-home.tsx | 4 +++- .../email-providers-comparison/in-depth/index.tsx | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) 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 8dfee19851ea9..9c62a2b48c67f 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/email-management/email-home.tsx b/client/my-sites/email/email-management/email-home.tsx index 58da9b57f2967..f5974e7498b8c 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 && ( Date: Thu, 6 Feb 2025 09:48:05 -0500 Subject: [PATCH 28/38] Fix hundred year plan layout (#99369) --- .../steps-repository/hundred-year-plan-setup/styles.scss | 3 +++ 1 file changed, 3 insertions(+) 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 e08688917db52..d35112cd13f99 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; + } } } From 265f8db039d66ed97d645eccfe65669972395170 Mon Sep 17 00:00:00 2001 From: Filippo Di Trapani Date: Thu, 6 Feb 2025 09:48:11 -0500 Subject: [PATCH 29/38] Reveal domain transfer link in hundred year plan flow (#99368) --- .../internals/steps-repository/domains/style.scss | 11 ----------- 1 file changed, 11 deletions(-) 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 5c56797838a48..a4a6822046782 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; - } - } } } From 6c202872562af96eb91dbe1385f14a9efcaeb615 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Thu, 6 Feb 2025 09:52:49 -0500 Subject: [PATCH 30/38] E2E: Fix media settings test on Atomic (#99413) PR #99393 switched from clicking the link in Calypso to visiting wp-admin directly. On Atomic this runs into the SSO login screen. Copy-paste the workaround used in other tests to avoid that. --- test/e2e/specs/media/settings__media.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/e2e/specs/media/settings__media.ts b/test/e2e/specs/media/settings__media.ts index 1f9d3e5394b30..bf298adf4b638 100644 --- a/test/e2e/specs/media/settings__media.ts +++ b/test/e2e/specs/media/settings__media.ts @@ -43,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' ) { From 36929c2c0b7c75af38616bd3b74e553bc696f1f7 Mon Sep 17 00:00:00 2001 From: niranjan-uma-shankar Date: Thu, 6 Feb 2025 18:58:34 +0400 Subject: [PATCH 31/38] Global styles should show under the Personal plan if it's enabled. Fetch from the config if the window object value is not available (#99409) --- .../src/is-global-styles-on-personal-enabled.ts | 9 +++++++++ 1 file changed, 9 insertions(+) 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 6443d064ce91c..874b40c825df6 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; } From 2be947cc3fadb3894372d9d39244ecc692b24020 Mon Sep 17 00:00:00 2001 From: Christos Date: Thu, 6 Feb 2025 17:07:38 +0200 Subject: [PATCH 32/38] i18n: Update several cases of `formattedNumber` usage with `numberFormat` (#99273) --- apps/odyssey-stats/src/widget/highlights.tsx | 8 +++++--- .../stats/sections/all-time-highlights-section/index.tsx | 4 ++-- client/my-sites/stats/stats-plan-usage/index.tsx | 7 +++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/odyssey-stats/src/widget/highlights.tsx b/apps/odyssey-stats/src/widget/highlights.tsx index 4c1f7a30d4baa..bdc5b9eae44c7 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/client/my-sites/stats/sections/all-time-highlights-section/index.tsx b/client/my-sites/stats/sections/all-time-highlights-section/index.tsx index 4afc29d01afe2..d7e60ad5972b1 100644 --- a/client/my-sites/stats/sections/all-time-highlights-section/index.tsx +++ b/client/my-sites/stats/sections/all-time-highlights-section/index.tsx @@ -1,4 +1,4 @@ -import { Card, ComponentSwapper, formattedNumber, DotPager } from '@automattic/components'; +import { Card, ComponentSwapper, DotPager } from '@automattic/components'; import { formatPercentage, percentCalculator, @@ -254,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/stats-plan-usage/index.tsx b/client/my-sites/stats/stats-plan-usage/index.tsx index b444f226a0e59..ba9dafde57bb8 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 ) : '-', }, } ) }
    From bd2d66a6652851582165fba9db1c9dbedc6919b3 Mon Sep 17 00:00:00 2001 From: Rafael Agostini Date: Thu, 6 Feb 2025 10:14:50 -0500 Subject: [PATCH 33/38] Stats Locations: Improve module title, tooltip, and responsive layout (#99386) * Stats Locations: Improve title and header layout - Update locations title to use a static translated label - Adjust header layout for better responsiveness - Modify segmented control and header styles for small breakpoints * Update module tooltip * Fix geochart responsive layout * Fix header margin and vertical alignment --- .../stats-locations/stats-locations.tsx | 6 ++-- .../modules/stats-locations/style.scss | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) 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 a97c59116131f..05eeb44cde478 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 @@ -99,7 +99,7 @@ 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 ) @@ -218,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: ( @@ -331,7 +331,7 @@ const StatsLocations: React.FC< StatsModuleLocationsProps > = ( { query, summary { ! isRequestingData && ! hasLocationData && ! shouldGate && ( // show empty state Date: Thu, 6 Feb 2025 23:33:22 +0800 Subject: [PATCH 34/38] E2E Tests: Fix the publish button is unavailable due to welcome guide (#99408) --- .../editor-welcome-guide-component.ts | 40 +++++++++++++++++++ .../calypso-e2e/src/lib/components/index.ts | 1 + .../calypso-e2e/src/lib/pages/editor-page.ts | 10 +++++ .../e2e/specs/onboarding/onboarding__write.ts | 1 + .../signup__start-writing-tailored.ts | 1 + 5 files changed, 53 insertions(+) create mode 100644 packages/calypso-e2e/src/lib/components/editor-welcome-guide-component.ts 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 0000000000000..90211a9f7bdd4 --- /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 ce968888a8dbc..71f9608692544 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 0f72675f313b8..6a78f8293369a 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/test/e2e/specs/onboarding/onboarding__write.ts b/test/e2e/specs/onboarding/onboarding__write.ts index 9b792433b06d7..5b55d0f8e9186 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 4fbdd03c3869d..22a56de837258 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(); From 3d55d30f893764e237634b0f4f93c5ca10788907 Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Fri, 7 Feb 2025 00:53:20 +0900 Subject: [PATCH 35/38] Concierge: Refactor closure notice (#99371) * Concierge: Refactor closure notice * Fixup * Add prop for easier discovery --- client/me/concierge/shared/closure-notice.js | 120 ++++++++++++------ .../me/concierge/shared/gm-closure-notice.js | 65 ---------- client/me/concierge/shared/primary-header.js | 1 + 3 files changed, 83 insertions(+), 103 deletions(-) delete mode 100644 client/me/concierge/shared/gm-closure-notice.js diff --git a/client/me/concierge/shared/closure-notice.js b/client/me/concierge/shared/closure-notice.js index 49b09fbe65962..8e1d3d05647d1 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 c901bbaf1ec4f..0000000000000 --- 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 021d5ed7a04d6..c1ffc83aa6fd2 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 } /> Date: Thu, 6 Feb 2025 10:20:54 -0600 Subject: [PATCH 36/38] Reader Onboarding: Remove wide screen flex style (#99381) * Remove wide screen flex style * Float progress circle to right * Remove breakpoints import and text-wrap pretty * Remove extra CSS nest --- client/reader/onboarding/style.scss | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/client/reader/onboarding/style.scss b/client/reader/onboarding/style.scss index af0db23c492f4..161d9552e4999 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; } } From 28997a847dfb90ee2df642f8dcaa9fe9a3b48cd0 Mon Sep 17 00:00:00 2001 From: sdnunca Date: Thu, 6 Feb 2025 18:41:26 +0200 Subject: [PATCH 37/38] A4A: Fix sign-up typo (#99418) Co-authored-by: sdnunca --- .../components/multi-step-form/blueprint-form/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx index 449c06295d739..0bb2617026e9b 100644 --- a/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx +++ b/client/a8c-for-agencies/sections/signup/signup-v2/components/multi-step-form/blueprint-form/index.tsx @@ -35,7 +35,7 @@ const BlueprintForm: React.FC< Props > = ( { onContinue } ) => { return (
    Date: Thu, 6 Feb 2025 16:53:08 +0000 Subject: [PATCH 38/38] Fix: Handle introductory offer periods longer than billing periods (#99093) --- .../variant-dropdown-price.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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 a6ada6482db61..2c6bc9e4dbf5a 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