client/components/mma/cancel/cancellationSaves/CancelAlternativeConfirmed.tsx (368 lines of code) (raw):
import { css } from '@emotion/react';
import {
between,
from,
palette,
space,
textEgyptian17,
textEgyptianBold17,
textSansBold17,
} from '@guardian/source/foundations';
import {
Button,
LinkButton,
SvgInfoRound,
} from '@guardian/source/react-components';
import { useContext, useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { measure } from '@/client/styles/typography';
import type { DiscountPreviewResponse } from '@/client/utilities/discountPreview';
import { getMaxNonDiscountedPrice } from '@/client/utilities/discountPreview';
import { DATE_FNS_LONG_OUTPUT_FORMAT, parseDate } from '@/shared/dates';
import type { PaidSubscriptionPlan } from '@/shared/productResponse';
import { getMainPlan } from '@/shared/productResponse';
import { BenefitsSection } from '../../shared/benefits/BenefitsSection';
import { DownloadAppCta } from '../../shared/DownloadAppCta';
import { DownloadFeastAppCtaWithIcon } from '../../shared/DownloadFeastAppCtaWithIcon';
import { Heading } from '../../shared/Heading';
import type {
CancellationContextInterface,
CancellationPageTitleInterface,
} from '../CancellationContainer';
import {
CancellationContext,
CancellationPageTitleContext,
} from '../CancellationContainer';
import { getUpdateCasePromise } from '../caseUpdate';
interface RouterState extends DiscountPreviewResponse {
caseId: string;
}
const standfirstCss = css`
${textEgyptian17};
color: ${palette.neutral[7]};
margin-top: 0;
`;
const nextStepsCss = css`
margin: ${space[4]}px 0 ${space[8]}px;
h4 {
${textEgyptianBold17};
margin: 0;
}
ul {
${textEgyptian17};
padding: 0;
padding-inline-start: 14px;
margin: ${space[3]}px 0 0;
line-height: 1.8rem;
}
${from.desktop} {
margin: ${space[6]}px 0 ${space[8]}px;
}
`;
const nextStepsWithSuffixText = css`
padding-bottom: ${space[6]}px;
margin-bottom: ${space[6]}px;
${from.desktop} {
padding-bottom: ${space[6]}px;
margin-bottom: ${space[6]}px;
}
`;
const benefitsCss = css`
display: flex;
flex-direction: column;
background-color: ${palette.culture[800]};
padding: ${space[1]}px ${space[3]}px ${space[3]}px;
h4 {
${textSansBold17};
margin: 0;
}
ul {
margin-top: ${space[4]}px;
}
${from.desktop} {
flex-direction: row;
justify-content: space-between;
padding: 0;
picture {
order: 2;
}
}
`;
const pictureAlignmentCss = css`
display: flex;
justify-content: center;
align-items: flex-end;
${between.desktop.and.leftCol} {
align-items: center;
}
${from.leftCol} {
max-width: 361px;
}
`;
const benefitsLeftSideCss = css`
${from.desktop} {
padding: ${space[6]}px;
}
`;
const mobileHeroHRCss = css`
height: 1px;
width: calc(100% - 40px);
background-color: ${palette.neutral[60]};
margin: 0 auto ${space[3]}px;
${from.desktop} {
display: none;
}
`;
const appAdCss = css`
margin-top: ${space[5]}px;
${from.desktop} {
margin-top: ${space[6]}px;
}
`;
const dontForgetCss = css`
display: flex;
gap: ${space[2]}px;
margin-top: ${space[6]}px;
border: 1px solid ${palette.neutral[86]};
padding: ${space[4]}px ${space[4]}px ${space[4]}px ${space[3]}px;
svg {
flex-shrink: 0;
}
p {
margin: 0;
}
${from.desktop} {
padding: ${space[4]}px ${space[4]}px ${space[4]}px ${space[5]}px;
}
`;
const onwardJourneyBtnsContainerCss = css`
display: flex;
flex-direction: column;
gap: ${space[5]}px;
margin-top: ${space[12]}px;
${from.phablet} {
flex-direction: row;
gap: ${space[4]}px;
}
`;
const buttonCentredCss = css`
width: 100%;
justify-content: center;
margin: 0;
${from.desktop} {
width: fit-content;
}
`;
const updateSalesforceCase = async (
isTestUser: boolean,
caseId: string,
loggingCodeSuffix: string,
description: string,
subject: string,
) => {
await getUpdateCasePromise(isTestUser, loggingCodeSuffix, caseId, {
Description: description,
Subject: subject,
});
};
export const CancelAlternativeConfirmed = () => {
const location = useLocation();
const routerState = location.state as RouterState;
const navigate = useNavigate();
const pageTitleContext = useContext(
CancellationPageTitleContext,
) as CancellationPageTitleInterface;
const cancellationContext = useContext(
CancellationContext,
) as CancellationContextInterface;
const productDetail = cancellationContext.productDetail;
const productType = cancellationContext.productType;
const mainPlan = getMainPlan(
productDetail.subscription,
) as PaidSubscriptionPlan;
const nextNonDiscountedPaymentDate = parseDate(
routerState.nextNonDiscountedPaymentDate,
'yyyy-MM-dd',
).dateStr(DATE_FNS_LONG_OUTPUT_FORMAT);
const firstDiscountedPaymentDate = parseDate(
routerState.firstDiscountedPaymentDate,
'yyyy-MM-dd',
).dateStr(DATE_FNS_LONG_OUTPUT_FORMAT);
const humanReadableNextNonDiscountedPrice = getMaxNonDiscountedPrice(
routerState.nonDiscountedPayments,
true,
);
const offerPeriodType = routerState.upToPeriodsType;
const alternativeIsOffer = productType.productType === 'supporterplus';
const alternativeIsPause = productType.productType === 'contributions';
const offerIsPercentageOrFree: 'percentage' | 'free' | false =
alternativeIsOffer &&
(routerState.discountPercentage < 100 ? 'percentage' : 'free');
const sfCaseDebugSuffix = `_${alternativeIsOffer ? 'OFFER' : ''}${
alternativeIsPause ? 'PAUSE' : ''
}`;
const sfCaseDescription = `User ${alternativeIsOffer ? 'took offer' : ''}${
alternativeIsPause ? 'paused' : ''
} instead of cancelling`;
const sfCaseSubject = `Online Cancellation Save Discount - ${
alternativeIsOffer ? 'Free' : ''
}${alternativeIsPause ? 'Pause' : ''} for ${
routerState.upToPeriods
} ${offerPeriodType}`;
useEffect(() => {
pageTitleContext.setPageTitle('Confirmation');
updateSalesforceCase(
productDetail.isTestUser,
routerState.caseId,
sfCaseDebugSuffix,
sfCaseDescription,
sfCaseSubject,
);
}, [
pageTitleContext,
productDetail.isTestUser,
routerState.caseId,
sfCaseDebugSuffix,
sfCaseDescription,
sfCaseSubject,
]);
return (
<>
<Heading
borderless
cssOverrides={[
measure.heading,
css`
margin: ${space[8]}px 0 ${space[2]}px;
`,
]}
>
Thank you for choosing to stay with us
</Heading>
<h3 css={standfirstCss}>
Your valued support powers independent journalism.
</h3>
<div
css={[
nextStepsCss,
alternativeIsPause && nextStepsWithSuffixText,
]}
>
<h4>What happens next?</h4>
<ul>
<li>
You will receive an email confirming
{alternativeIsOffer ? ' the details of your offer' : ''}
{alternativeIsPause
? " you've paused your support"
: ''}
</li>
{alternativeIsOffer && (
<li>
You will continue enjoying all the benefits of your
All-access digital subscription
{offerIsPercentageOrFree === 'free' &&
' – for free'}
</li>
)}
{alternativeIsOffer &&
offerIsPercentageOrFree === 'percentage' && (
<li>
You will be billed at the discounted rate on{' '}
{firstDiscountedPaymentDate}
</li>
)}
{((alternativeIsOffer &&
offerIsPercentageOrFree === 'free') ||
alternativeIsPause) && (
<li>
You will not be billed until{' '}
{nextNonDiscountedPaymentDate} after which you will
pay {mainPlan.currency}
{humanReadableNextNonDiscountedPrice}/
{mainPlan.billingPeriod}
</li>
)}
</ul>
</div>
{alternativeIsOffer && (
<>
<div css={benefitsCss}>
<picture css={pictureAlignmentCss}>
<source
srcSet="https://media.guim.co.uk/4642d75e4282cf62980b6aa60eb5f710a6795e82/0_0_1444_872/1000.png"
media="(min-width: 1140px)"
/>
<source
srcSet="https://media.guim.co.uk/7a20e5ce7fd500ec7bac3ec372d7d1e041f5bfe5/0_0_1252_1100/500.png"
media="(min-width: 980px) and (max-width: 1139px)"
/>
<img src="https://media.guim.co.uk/63d17ee19313703129fbbeacceaafcd6d1cc1014/0_0_1404_716/500.png" />
</picture>
<div css={mobileHeroHRCss}></div>
<div css={benefitsLeftSideCss}>
<h4>
With your offer, you will continue to enjoy:
</h4>
<BenefitsSection
small
benefits={[
{
description:
'Unlimited access to the Guardian app',
},
{
description:
'Ad-free reading across all your devices',
},
{
description:
'Exclusive supporter newsletter',
},
{
description:
"Far fewer asks for support when you're signed in",
},
]}
/>
</div>
</div>
<DownloadAppCta additionalCss={appAdCss} />
<DownloadFeastAppCtaWithIcon additionalCss={appAdCss} />
<div css={dontForgetCss}>
<SvgInfoRound
size="small"
theme={{ fill: palette.brand[400] }}
/>
<p>
Don't forget to sign in on all your devices to enjoy
your benefits.
</p>
</div>
</>
)}
{alternativeIsPause && (
<p css={dontForgetCss}>
Don't forget to sign in on all your devices to get the best
experience
</p>
)}
<div css={onwardJourneyBtnsContainerCss}>
<LinkButton
href="https://theguardian.com"
priority="primary"
cssOverrides={buttonCentredCss}
>
Continue reading the Guardian
</LinkButton>
<Button
cssOverrides={buttonCentredCss}
priority="tertiary"
onClick={() => navigate('/')}
>
Return to your account
</Button>
</div>
</>
);
};