client/components/mma/delivery/address/DeliveryAddressConfirmation.tsx (340 lines of code) (raw):

import type { SerializedStyles } from '@emotion/react'; import { css } from '@emotion/react'; import { from, headlineBold28, palette, space, textSans17, textSansBold17, until, } from '@guardian/source/foundations'; import { Stack } from '@guardian/source/react-components'; import { useContext, useState } from 'react'; import { Navigate, useLocation } from 'react-router-dom'; import { dateString } from '../../../../../shared/dates'; import type { ProductDetail } from '../../../../../shared/productResponse'; import type { ProductType, WithProductType, } from '../../../../../shared/productTypes'; import { trackEvent } from '../../../../utilities/analytics'; import { CallCentreEmailAndNumbers } from '../../../shared/CallCenterEmailAndNumbers'; import { TickInCircle } from '../../shared/assets/TickInCircle'; import { AsyncLoader } from '../../shared/AsyncLoader'; import { LinkButton } from '../../shared/Buttons'; import { ProductDescriptionListTable } from '../../shared/ProductDescriptionListTable'; import { ProgressIndicator } from '../../shared/ProgressIndicator'; import { updateAddressFetcher } from './deliveryAddressApi'; import { DeliveryAddressDisplay } from './DeliveryAddressDisplay'; import { AddressChangedInformationContext, ContactIdContext, convertToDescriptionListData, isAddress, NewDeliveryAddressContext, } from './DeliveryAddressFormContext'; const renderConfirmation = (props: ProductType) => () => <AddressConfirmation {...props} />; const AddressConfirmation = (props: ProductType) => { const location = useLocation(); const productDetail = location.state as ProductDetail; const addressContext = useContext(NewDeliveryAddressContext); const addressChangedInformationContext = useContext( AddressChangedInformationContext, ); const productName = props.friendlyName; if (isAddress(addressContext.addressStateObject)) { productDetail.subscription.deliveryAddress = { ...productDetail.subscription.deliveryAddress, ...addressContext.addressStateObject, }; } const [showTopCallCentreNumbers, setTopCallCentreNumbersVisibility] = useState<boolean>(false); const subHeadingCss = ` border-top: 1px solid ${palette.neutral['86']}; ${headlineBold28}; margin-top: 50px; ${until.tablet} { font-size: 1.25rem; line-height: 1.6; }; `; const dtCss: string = ` font-weight: bold; display: inline-block; vertical-align: top; min-width: 12ch; `; const ddCss: string = ` margin: 0; display: inline-block; vertical-align: top; min-width: 12ch; `; return ( <> {isAddress(addressContext.addressStateObject) ? ( <> <ProgressIndicator steps={[ { title: 'Update' }, { title: 'Review' }, { title: 'Confirmation', isCurrentStep: true }, ]} additionalCSS={css` margin-top: ${space[5]}px; `} /> <h2 css={css` ${subHeadingCss} `} > Confirmation </h2> <SuccessMessage message={`We have successfully updated your delivery details for your ${productName}. You will shortly receive a confirmation email.`} /> <section css={css` border: 1px solid ${palette.neutral['86']}; margin-top: ${space[5]}px; `} > <h2 css={css` margin: 0; padding: ${space[3]}px; background-color: ${palette.neutral['97']}; border-bottom: 1px solid ${palette.neutral['86']}; ${textSansBold17}; ${from.tablet} { padding: ${space[3]}px ${space[5]}px; } `} > Delivery address {props.delivery?.enableDeliveryInstructionsUpdate && ' and instructions'} </h2> <dl css={css` padding: 0 ${space[3]}px; ${textSans17}; display: flex; flex-wrap: wrap; flex-direction: column; justify-content: space-between; ${from.tablet} { padding: 0 ${space[5]}px; } `} > <div css={css` flex-grow: 1; `} > <dt css={css` ${dtCss} `} > Address </dt> <dd css={css` ${ddCss} `} > {addressContext.addressStateObject && ( <DeliveryAddressDisplay {...addressContext.addressStateObject} /> )} </dd> </div> <div css={css` flex-grow: 1; margin-top: 16px; ${from.tablet} { margin-top: 0; } `} > <dt css={css` ${dtCss} `} > Instructions </dt> <dd css={css` ${ddCss} `} > {addressContext.addressStateObject ?.instructions || '-'} </dd> </div> </dl> </section> <p css={css` ${textSans17}; margin-top: ${space[9]}px; `} > I understand that this address change will affect the following subscriptions </p> <ProductDescriptionListTable content={convertToDescriptionListData( addressChangedInformationContext, )} seperateEachRow /> <div css={css` margin-top: ${space[3]}px; ${from.tablet} { margin-top: ${space[5]}px; } `} > <LinkButton to={`/${props.urlPart}`} text={'Return to subscription'} state={{ productDetail }} colour={palette.brand[400]} textColour={palette.neutral[100]} fontWeight={'bold'} onClick={() => { trackEvent({ eventCategory: 'delivery_address_update_confirmation', eventAction: 'click', eventLabel: `manage_${props.urlPart}`, }); }} /> </div> <Stack space={5}> <p css={css` ${textSans17}; margin: ${space[12]}px 0 0; color: ${palette.neutral[46]}; `} > If you need separate delivery addresses for each of your subscriptions, please{' '} <span css={css` cursor: pointer; color: ${palette.brand[500]}; text-decoration: underline; `} onClick={() => setTopCallCentreNumbersVisibility( !showTopCallCentreNumbers, ) } > contact us </span> . </p> {showTopCallCentreNumbers && ( <CallCentreEmailAndNumbers /> )} </Stack> </> ) : ( <Navigate to=".." replace /> )} </> ); }; export const DeliveryAddressConfirmation = ( props: WithProductType<ProductType>, ) => { const addressContext = useContext(NewDeliveryAddressContext); const contactIdContext = useContext(ContactIdContext); const addressChangedInformationContext = useContext( AddressChangedInformationContext, ); const contactId = Object.keys(contactIdContext)[0]; const addressChangeInformationCopy = [ ...addressChangedInformationContext.map( (element) => `${element.friendlyProductName} subscription (${ element.subscriptionId })${ element.effectiveDate ? ` as of front cover dated ${dateString( element.effectiveDate, 'iiii do MMMM yyyy', )}` : '' }`, ), '', `(as displayed on confirmation page at ${dateString( new Date(), "HH:mm:ss x 'on' do MMMM yyyy", )} )`, ].join('\n'); return addressContext.addressStateObject ? ( <AsyncLoader render={renderConfirmation(props.productType)} fetch={updateAddressFetcher( { ...addressContext.addressStateObject, addressChangeInformation: addressChangeInformationCopy, }, contactId, )} readerOnOK={(resp: Response) => resp.text()} loadingMessage={'Updating delivery address details...'} /> ) : ( <Navigate to=".." replace /> ); }; interface SuccessMessageProps { message: string; additionalCss?: SerializedStyles; } export const SuccessMessage = (props: SuccessMessageProps) => ( <div css={css` position: relative; width: 100%; text-align: left; border: 4px solid ${palette.success[400]}; box-sizing: border-box; padding: 14px 14px 14px 50px; margin-bottom: 50px; ${textSans17}; font-weight: bold; ${props.additionalCss} `} > <i css={css` position: absolute; top: 14px; left: 14px; `} > <TickInCircle /> </i> {props.message} </div> );