diff --git a/client/components/mma/accountoverview/AccountOverview.tsx b/client/components/mma/accountoverview/AccountOverview.tsx index a986d726b..2c0da5b2e 100644 --- a/client/components/mma/accountoverview/AccountOverview.tsx +++ b/client/components/mma/accountoverview/AccountOverview.tsx @@ -23,6 +23,7 @@ import { isSpecificProductType, sortByJoinDate, } from '../../../../shared/productResponse'; +import type { GroupedProductTypeKeys } from '../../../../shared/productTypes'; import { GROUPED_PRODUCT_TYPES, PRODUCT_TYPES, @@ -107,15 +108,17 @@ const AccountOverviewPage = ({ isFromApp }: IsFromAppProps) => { b.subscription.start.localeCompare(a.subscription.start), ); - const productCategories = [ + const allProductCategories = [ ...allActiveProductDetails, ...allCancelledProductDetails, - ] - .map( - (product: ProductDetail | CancelledProductDetail) => - product.mmaCategory, - ) - .filter((value, index, self) => self.indexOf(value) === index); + ].map((product: ProductDetail | CancelledProductDetail) => { + if (product.mmaCategory === 'recurringSupport') { + return 'subscriptions'; // we want to override the display text in MMA for RC/S+ but not affect functionality + } + return product.mmaCategory; + }); + + const uniqueProductCategories = [...new Set(allProductCategories)]; const appSubscriptions = mpapiResponse.subscriptions.filter( isValidAppSubscription, @@ -124,16 +127,16 @@ const AccountOverviewPage = ({ isFromApp }: IsFromAppProps) => { if ( featureSwitches.appSubscriptions && appSubscriptions.length > 0 && - !productCategories.includes('subscriptions') + !uniqueProductCategories.includes('subscriptions') ) { - productCategories.push('subscriptions'); + uniqueProductCategories.push('subscriptions'); } if ( singleContributions.length > 0 && - !productCategories.includes('subscriptions') + !uniqueProductCategories.includes('subscriptions') ) { - productCategories.push('subscriptions'); + uniqueProductCategories.push('subscriptions'); } if ( @@ -165,6 +168,15 @@ const AccountOverviewPage = ({ isFromApp }: IsFromAppProps) => { !hasDigiSubAndContribution && !hasNonServiceableCountry; + const visualProductGroupingCategory = ( + product: ProductDetail | CancelledProductDetail, + ): GroupedProductTypeKeys => { + if (product.mmaCategory === 'recurringSupport') { + return 'subscriptions'; + } + return product.mmaCategory; + }; + return ( <> { productDetails={allActiveProductDetails} isFromApp={isFromApp} /> - {productCategories.map((category) => { + {uniqueProductCategories.map((category) => { const groupedProductType = GROUPED_PRODUCT_TYPES[category]; const activeProductsInCategory = allActiveProductDetails.filter( - (activeProduct) => activeProduct.mmaCategory === category, + (activeProduct) => + visualProductGroupingCategory(activeProduct) === + category, ); const cancelledProductsInCategory = allCancelledProductDetails.filter( - (activeProduct) => - activeProduct.mmaCategory === category, + (cancelledProduct) => + visualProductGroupingCategory(cancelledProduct) === + category, ); return ( diff --git a/client/components/mma/accountoverview/ManageProduct.stories.tsx b/client/components/mma/accountoverview/ManageProduct.stories.tsx index 059a37d76..d6e0bcc36 100644 --- a/client/components/mma/accountoverview/ManageProduct.stories.tsx +++ b/client/components/mma/accountoverview/ManageProduct.stories.tsx @@ -5,6 +5,7 @@ import { PRODUCT_TYPES } from '../../../../shared/productTypes'; import { digitalPackPaidByDirectDebit, guardianWeeklyPaidByCard, + monthlyContributionPaidByCard, newspaperVoucherPaidByPaypal, supporterPlusAnnual, } from '../../../fixtures/productBuilder/testProducts'; @@ -55,6 +56,18 @@ export const NewspaperSubscriptionCard: StoryObj = { }, }; +export const Contribution: StoryObj = { + render: () => { + return ; + }, + + parameters: { + reactRouter: { + state: { productDetail: monthlyContributionPaidByCard() }, + }, + }, +}; + featureSwitches.supporterPlusUpdateAmount = true; export const SupporterPlus: StoryObj = { diff --git a/client/components/mma/accountoverview/ProductCard.tsx b/client/components/mma/accountoverview/ProductCard.tsx index 6b2bc8f40..6986cbfe7 100644 --- a/client/components/mma/accountoverview/ProductCard.tsx +++ b/client/components/mma/accountoverview/ProductCard.tsx @@ -20,10 +20,7 @@ import type { Subscription, } from '@/shared/productResponse'; import { getMainPlan, isGift } from '@/shared/productResponse'; -import { - calculateSupporterPlusTitle, - GROUPED_PRODUCT_TYPES, -} from '@/shared/productTypes'; +import { GROUPED_PRODUCT_TYPES } from '@/shared/productTypes'; import { wideButtonLayoutCss } from '../../../styles/ButtonStyles'; import { trackEvent } from '../../../utilities/analytics'; import { ErrorIcon } from '../shared/assets/ErrorIcon'; @@ -373,11 +370,7 @@ export const ProductCard = ({ }) } > - Change to{' '} - {calculateSupporterPlusTitle( - (mainPlan as PaidSubscriptionPlan) - .billingPeriod, - )} + Change to all-access digital )} diff --git a/client/components/mma/cancel/CancellationSummary.tsx b/client/components/mma/cancel/CancellationSummary.tsx index 3837afbbe..2c33b2e63 100644 --- a/client/components/mma/cancel/CancellationSummary.tsx +++ b/client/components/mma/cancel/CancellationSummary.tsx @@ -36,12 +36,9 @@ const actuallyCancelled = ( `, ]} > - {productType.cancellation?.alternateSummaryHeading( + {`Your ${productType.friendlyName( cancelledProductDetail, - ) || - `Your ${productType.friendlyName( - cancelledProductDetail, - )} is cancelled`} + )} is cancelled`} {productType.cancellation && !productType.cancellation.shouldHideSummaryMainPara && ( diff --git a/client/components/mma/cancel/contributions/ContributionsCancellationFlowStart.tsx b/client/components/mma/cancel/contributions/ContributionsCancellationFlowStart.tsx index 117184598..2d19c7042 100644 --- a/client/components/mma/cancel/contributions/ContributionsCancellationFlowStart.tsx +++ b/client/components/mma/cancel/contributions/ContributionsCancellationFlowStart.tsx @@ -5,13 +5,13 @@ import { Heading } from '../../shared/Heading'; export const contributionsCancellationFlowStart = () => ( - We’re sorry to see you go… + We’re sorry to see you go …

- …please could you take a moment to tell us why you would like to - cancel today? + … please could you take a moment to tell us why you would like + to cancel today?
As a reader-funded organisation, we rely on the generous support diff --git a/client/components/mma/cancel/supporterplus/SupporterplusCancellationFlowStart.tsx b/client/components/mma/cancel/supporterplus/SupporterplusCancellationFlowStart.tsx index fe00f6c1c..47e23bdcc 100644 --- a/client/components/mma/cancel/supporterplus/SupporterplusCancellationFlowStart.tsx +++ b/client/components/mma/cancel/supporterplus/SupporterplusCancellationFlowStart.tsx @@ -5,13 +5,13 @@ import { Heading } from '../../shared/Heading'; export const supporterplusCancellationFlowStart = () => ( - We’re sorry to see you go… + We’re sorry to see you go …

- …please could you take a moment to tell us why you would like to - cancel today? + … please could you take a moment to tell us why you would like + to cancel today?
As a reader-funded organisation, we rely on the generous support diff --git a/client/components/mma/shared/benefits/BenefitsToggle.tsx b/client/components/mma/shared/benefits/BenefitsToggle.tsx index 2b6a03907..2a096713e 100644 --- a/client/components/mma/shared/benefits/BenefitsToggle.tsx +++ b/client/components/mma/shared/benefits/BenefitsToggle.tsx @@ -28,7 +28,7 @@ export const BenefitsToggle = (props: { productType: ProductTypeKeys }) => { aria-controls="benefits" onClick={() => setShowBenefits(!showBenefits)} > - {showBenefits ? 'hide' : 'view'} extras + {showBenefits ? 'hide' : 'view'} benefits ); diff --git a/client/components/mma/switch/SwitchContainer.tsx b/client/components/mma/switch/SwitchContainer.tsx index c2eb3116c..17e42feef 100644 --- a/client/components/mma/switch/SwitchContainer.tsx +++ b/client/components/mma/switch/SwitchContainer.tsx @@ -160,7 +160,8 @@ const RenderedPage = (props: { user: props.user, mainPlan, monthlyOrAnnual, - supporterPlusTitle: `${monthlyOrAnnual} + extras`, + supporterPlusTitle: + PRODUCT_TYPES.supporterplus.productTitle(), thresholds: getThresholds( mainPlan, monthlyOrAnnual == 'Monthly', diff --git a/client/fixtures/productBuilder/testProducts.ts b/client/fixtures/productBuilder/testProducts.ts index 993500f79..c9a9b54fe 100644 --- a/client/fixtures/productBuilder/testProducts.ts +++ b/client/fixtures/productBuilder/testProducts.ts @@ -66,6 +66,13 @@ export function digitalPackPaidByCardWithPaymentFailure() { .getProductDetailObject(); } +export function monthlyContributionPaidByCard() { + return new ProductBuilder(baseContribution()) + .payByCard() + .withPrice(400) + .getProductDetailObject(); +} + export function annualContributionPaidByCardWithCurrency( currency: CurrencyIso, ) { diff --git a/cypress/tests/mocked/parallel-1/updateContributionAmount.cy.ts b/cypress/tests/mocked/parallel-1/updateContributionAmount.cy.ts index b9e437243..90cfdf7ed 100644 --- a/cypress/tests/mocked/parallel-1/updateContributionAmount.cy.ts +++ b/cypress/tests/mocked/parallel-1/updateContributionAmount.cy.ts @@ -66,7 +66,7 @@ describe('Update contribution amount', () => { setSignInStatus(); - cy.findByText('Manage recurring support').click(); + cy.findByText('Manage subscription').click(); cy.wait('@cancelled'); cy.findByText('Change amount').click(); @@ -93,7 +93,7 @@ describe('Update contribution amount', () => { setSignInStatus(); - cy.findByText('Manage recurring support').click(); + cy.findByText('Manage subscription').click(); cy.wait('@cancelled'); cy.findByText('Change amount').click(); diff --git a/cypress/tests/mocked/parallel-2/cancelContribution.cy.ts b/cypress/tests/mocked/parallel-2/cancelContribution.cy.ts index 99c98c00a..dadfd93e7 100644 --- a/cypress/tests/mocked/parallel-2/cancelContribution.cy.ts +++ b/cypress/tests/mocked/parallel-2/cancelContribution.cy.ts @@ -19,11 +19,11 @@ describe('Cancel contribution', () => { setSignInStatus(); - cy.findByText('Manage recurring support').click(); + cy.findByText('Manage subscription').click(); cy.wait('@cancelled'); cy.findByRole('link', { - name: 'Cancel recurring support', + name: 'Cancel subscription', }).click(); }; diff --git a/cypress/tests/mocked/parallel-2/cancelSupporterPlus.cy.ts b/cypress/tests/mocked/parallel-2/cancelSupporterPlus.cy.ts index cf04bad44..85838ee59 100644 --- a/cypress/tests/mocked/parallel-2/cancelSupporterPlus.cy.ts +++ b/cypress/tests/mocked/parallel-2/cancelSupporterPlus.cy.ts @@ -11,10 +11,10 @@ describe('Cancel Supporter Plus', () => { cy.wait('@mobile_subscriptions'); cy.wait('@single_contributions'); - cy.findByText('Manage recurring support').click(); + cy.findByText('Manage subscription').click(); cy.findByRole('link', { - name: 'Cancel recurring support', + name: 'Cancel subscription', }).click(); }; @@ -123,7 +123,7 @@ describe('Cancel Supporter Plus', () => { cy.wait('@get_cancelled_product'); cy.findByRole('heading', { - name: 'Monthly support + extras cancelled', + name: 'Your all-access digital subscription is cancelled', }); cy.get('@get_cancellation_date.all').should('have.length', 0); diff --git a/cypress/tests/mocked/parallel-2/productSwitch.cy.ts b/cypress/tests/mocked/parallel-2/productSwitch.cy.ts index 84ccfc7ad..dcb743588 100644 --- a/cypress/tests/mocked/parallel-2/productSwitch.cy.ts +++ b/cypress/tests/mocked/parallel-2/productSwitch.cy.ts @@ -67,8 +67,11 @@ describe('product switching', () => { cy.visit('/'); setSignInStatus(); - cy.findAllByText('Change to monthly + extras').should('have.length', 2); - cy.findAllByText('Change to monthly + extras').last().click(); + cy.findAllByText('Change to all-access digital').should( + 'have.length', + 2, + ); + cy.findAllByText('Change to all-access digital').last().click(); cy.findByText('Your current support').should('exist'); cy.findByRole('button', { @@ -205,11 +208,17 @@ describe('product switching', () => { cy.visit('/'); setSignInStatus(); - cy.findAllByText('Change to monthly + extras').should('have.length', 0); + cy.findAllByText('Change to all-access digital').should( + 'have.length', + 0, + ); cy.visit('/switch'); cy.findByRole('heading', { name: 'Account overview' }).should('exist'); - cy.findAllByText('Change to monthly + extras').should('have.length', 0); + cy.findAllByText('Change to all-access digital').should( + 'have.length', + 0, + ); }); }); diff --git a/cypress/tests/mocked/parallel-4/deliveryAddress.cy.ts b/cypress/tests/mocked/parallel-4/deliveryAddress.cy.ts index 312752ed8..b201e894c 100644 --- a/cypress/tests/mocked/parallel-4/deliveryAddress.cy.ts +++ b/cypress/tests/mocked/parallel-4/deliveryAddress.cy.ts @@ -1,7 +1,6 @@ import { guardianWeeklyPaidByCard, nationalDelivery, - supporterPlus, } from '../../../../client/fixtures/productBuilder/testProducts'; import { toMembersDataApiResponse } from '../../../../client/fixtures/mdapiResponse'; import { signInAndAcceptCookies } from '../../../lib/signInAndAcceptCookies'; @@ -12,10 +11,7 @@ describe('Delivery address', () => { cy.intercept('GET', '/api/me/mma?productType=ContentSubscription', { statusCode: 200, - body: toMembersDataApiResponse( - guardianWeeklyPaidByCard(), - supporterPlus(), - ), + body: toMembersDataApiResponse(guardianWeeklyPaidByCard()), }).as('product_detail'); cy.intercept('GET', '/mpapi/user/mobile-subscriptions', { @@ -42,10 +38,7 @@ describe('Delivery address', () => { it('Can update delivery address. Navigating from account overview', () => { cy.intercept('GET', '/api/me/mma', { statusCode: 200, - body: toMembersDataApiResponse( - guardianWeeklyPaidByCard(), - supporterPlus(), - ), + body: toMembersDataApiResponse(guardianWeeklyPaidByCard()), }).as('mma'); cy.visit('/'); @@ -80,7 +73,7 @@ describe('Delivery address', () => { it('Cannot update National delivery address. Navigating from account overview', () => { cy.intercept('GET', '/api/me/mma', { statusCode: 200, - body: toMembersDataApiResponse(nationalDelivery(), supporterPlus()), + body: toMembersDataApiResponse(nationalDelivery()), }).as('mma'); cy.visit('/'); @@ -93,7 +86,7 @@ describe('Delivery address', () => { cy.intercept('GET', '/api/me/mma?productType=ContentSubscription', { statusCode: 200, - body: toMembersDataApiResponse(nationalDelivery(), supporterPlus()), + body: toMembersDataApiResponse(nationalDelivery()), }); cy.findByText('Manage delivery address').click(); @@ -107,7 +100,6 @@ describe('Delivery address', () => { body: toMembersDataApiResponse( nationalDelivery(), guardianWeeklyPaidByCard(), - supporterPlus(), ), }).as('mma'); @@ -119,7 +111,7 @@ describe('Delivery address', () => { cy.intercept('GET', '/api/me/mma?productType=ContentSubscription', { statusCode: 200, - body: toMembersDataApiResponse(nationalDelivery(), supporterPlus()), + body: toMembersDataApiResponse(nationalDelivery()), }); cy.findByText(/Changed address?/).should('exist'); @@ -128,10 +120,7 @@ describe('Delivery address', () => { it('Shows updated address when returning to manage subscription page', () => { cy.intercept('GET', '/api/me/mma', { statusCode: 200, - body: toMembersDataApiResponse( - guardianWeeklyPaidByCard(), - supporterPlus(), - ), + body: toMembersDataApiResponse(guardianWeeklyPaidByCard()), }).as('mma'); cy.visit('/'); diff --git a/shared/productTypes.ts b/shared/productTypes.ts index f0971afd1..3711c131b 100644 --- a/shared/productTypes.ts +++ b/shared/productTypes.ts @@ -1,4 +1,3 @@ -import { capitalize } from 'lodash'; import type { ReactNode } from 'react'; import type { CancellationReason, @@ -28,7 +27,7 @@ import type { SubscriptionPlan, SubscriptionWithDeliveryAddress, } from './productResponse'; -import { getMainPlan, isGift } from './productResponse'; +import { isGift } from './productResponse'; import { SoftOptInIDs } from './softOptInIDs'; type ProductFriendlyName = @@ -39,8 +38,7 @@ type ProductFriendlyName = | 'newspaper subscription card' | 'newspaper home delivery subscription' | 'digital subscription' - | 'monthly + extras' - | 'annual + extras' + | 'all-access digital subscription' | 'Guardian Weekly subscription' | 'subscription' | 'recurring support' @@ -93,9 +91,6 @@ interface CancellationFlowProperties { startPageOfferEffectiveDateOptions?: true; hideReasonTitlePrefix?: true; alternateSummaryMainPara?: string; - alternateSummaryHeading: ( - productDetail: ProductDetail, - ) => string | undefined; shouldHideSummaryMainPara?: true; summaryReasonSpecificPara: ( reasonId: OptionalCancellationReasonId, @@ -233,17 +228,6 @@ const calculateProductTitle = (baseProductTitle: string) => (mainPlan?: SubscriptionPlan) => baseProductTitle + (mainPlan?.name ? ` - ${mainPlan.name}` : ''); -export function calculateSupporterPlusTitle(billingPeriod: string) { - if (billingPeriod === 'month') { - return 'monthly + extras'; - } - if (billingPeriod === 'year') { - return 'annual + extras'; - } - - return 'recurring support'; -} - export function getBillingPeriodAdjective( billingPeriod: string | undefined, ): 'Monthly' | 'Annual' | 'Quarterly' { @@ -302,7 +286,6 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { reasons: membershipCancellationReasons, sfCaseProduct: 'Membership', startPageBody: membershipCancellationFlowStart, - alternateSummaryHeading: () => undefined, hideReasonTitlePrefix: true, summaryReasonSpecificPara: () => undefined, onlyShowSupportSectionIfAlternateText: false, @@ -347,7 +330,6 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { sfCaseProduct: 'Recurring - Contributions', startPageBody: contributionsCancellationFlowStart, shouldHideSummaryMainPara: true, - alternateSummaryHeading: () => undefined, summaryReasonSpecificPara: ( reasonId: OptionalCancellationReasonId, ) => { @@ -528,7 +510,6 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { startPageOfferEffectiveDateOptions: true, summaryReasonSpecificPara: () => undefined, onlyShowSupportSectionIfAlternateText: false, - alternateSummaryHeading: () => undefined, alternateSupportButtonText: () => undefined, alternateSupportButtonUrlSuffix: () => undefined, swapFeedbackAndContactUs: true, @@ -601,7 +582,6 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { startPageOfferEffectiveDateOptions: true, summaryReasonSpecificPara: () => undefined, onlyShowSupportSectionIfAlternateText: false, - alternateSummaryHeading: () => undefined, alternateSupportButtonText: () => undefined, alternateSupportButtonUrlSuffix: () => undefined, swapFeedbackAndContactUs: true, @@ -633,33 +613,14 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { startPageBody: digipackCancellationFlowStart, summaryReasonSpecificPara: () => undefined, onlyShowSupportSectionIfAlternateText: false, - alternateSummaryHeading: () => undefined, alternateSupportButtonText: () => undefined, alternateSupportButtonUrlSuffix: () => undefined, swapFeedbackAndContactUs: true, }, }, supporterplus: { - productTitle: (mainPlan?: SubscriptionPlan) => { - if (!mainPlan) { - return 'Recurring support'; - } - - const paidMainPlan = mainPlan as PaidSubscriptionPlan; - return `${capitalize( - calculateSupporterPlusTitle(paidMainPlan.billingPeriod), - )}`; - }, - friendlyName: (productDetail?: ProductDetail) => { - if (!productDetail) { - return 'recurring support'; - } - - const billingPeriod = ( - getMainPlan(productDetail.subscription) as PaidSubscriptionPlan - ).billingPeriod; - return calculateSupporterPlusTitle(billingPeriod); - }, + productTitle: () => 'All-access digital', + friendlyName: () => 'all-access digital subscription', productType: 'supporterplus', groupedProductType: 'recurringSupport', allProductsProductTypeFilterString: 'SupporterPlus', @@ -674,16 +635,6 @@ export const PRODUCT_TYPES: { [productKey in ProductTypeKeys]: ProductType } = { cancellation: { alternateSummaryMainPara: "This is immediate and you will not be charged again. If you've cancelled within the first 14 days, we'll send you a full refund.", - alternateSummaryHeading: (productDetail: ProductDetail) => { - const billingPeriod = ( - getMainPlan( - productDetail.subscription, - ) as PaidSubscriptionPlan - ).billingPeriod; - return `${getBillingPeriodAdjective( - billingPeriod, - )} support + extras cancelled`; - }, linkOnProductPage: true, reasons: shuffledSupporterPlusCancellationReasons, sfCaseProduct: 'Supporter Plus', @@ -728,9 +679,9 @@ export const GROUPED_PRODUCT_TYPES: { }, }, recurringSupport: { - productTitle: () => 'Recurring support', - friendlyName: () => 'recurring support', - groupFriendlyName: 'recurring support', + productTitle: () => 'Subscription', + friendlyName: () => 'subscription', + groupFriendlyName: 'subscription', allProductsProductTypeFilterString: 'SupporterPlus', // this will only return SupporterPlus, and not Contributions urlPart: 'recurringsupport', mapGroupedToSpecific: (