client/components/mma/delivery/records/DeliveryRecordsProblemConfirmation.tsx (461 lines of code) (raw):

import { css } from '@emotion/react'; import { from, headlineBold28, palette, space, textSans15, textSans17, textSansBold17, until, } from '@guardian/source/foundations'; import { LinkButton } from '@guardian/source/react-components'; import { useContext } from 'react'; import { Navigate, useLocation } from 'react-router-dom'; import { DATE_FNS_SHORT_OUTPUT_FORMAT, dateString } from '@/shared/dates'; import type { DeliveryRecordApiItem, PaidSubscriptionPlan, Subscription, } from '@/shared/productResponse'; import { getMainPlan } from '@/shared/productResponse'; import { NAV_LINKS } from '../../../shared/nav/NavConfig'; import { InfoIconDark } from '../../shared/assets/InfoIconDark'; import { ProductDescriptionListTable } from '../../shared/ProductDescriptionListTable'; import { ProgressIndicator } from '../../shared/ProgressIndicator'; import { DeliveryRecordCard } from './DeliveryRecordCard'; import { PageStatus } from './DeliveryRecords'; import type { DeliveryRecordsPostPayload, DeliveryRecordsResponse, } from './deliveryRecordsApi'; import { createDeliveryRecordsProblemPost, DeliveryRecordsApiAsyncLoader, } from './deliveryRecordsApi'; import type { DeliveryRecordsContextInterface, DeliveryRecordsRouterState, } from './DeliveryRecordsContainer'; import { DeliveryRecordsContext } from './DeliveryRecordsContainer'; import { DeliveryRecordCreditContext, DeliveryRecordsAddressContext, DeliveryRecordsProblemPostPayloadContext, } from './DeliveryRecordsProblemContext'; import { ReadOnlyAddressDisplay } from './ReadOnlyAddressDisplay'; const renderDeliveryRecordsConfirmation = (subscription: Subscription) => (data: DeliveryRecordsResponse) => { const mainPlan = getMainPlan(subscription) as PaidSubscriptionPlan; if (!mainPlan) { throw new Error( 'mainPlan does not exist in deliveryRecordsProblemReview', ); } return ( <DeliveryRecordsProblemConfirmationFC data={data} subscriptionId={subscription.subscriptionId} subscriptionCurrency={mainPlan.currency} /> ); }; interface DeliveryRecordsProblemConfirmationFCProps { data: DeliveryRecordsResponse; subscriptionId: string; subscriptionCurrency: string; } const DeliveryRecordsProblemConfirmationFC = ( props: DeliveryRecordsProblemConfirmationFCProps, ) => { const deliveryIssuePostPayload = useContext( DeliveryRecordsProblemPostPayloadContext, ); const deliveryProblemCredit = useContext(DeliveryRecordCreditContext); const deliveryAddressContext = useContext(DeliveryRecordsAddressContext); const filteredData = props.data.results.filter( (record) => deliveryIssuePostPayload?.deliveryRecords?.findIndex( (affectedRecord) => affectedRecord.id === record.id, ) !== -1, ); const { productType } = useContext( DeliveryRecordsContext, ) as DeliveryRecordsContextInterface; const problemCaseId = filteredData.find( (record) => record.problemCaseId, )?.problemCaseId; const problemReferenceId = problemCaseId ? props.data.deliveryProblemMap[problemCaseId]?.ref : '-'; const dtCss = css` font-weight: bold; display: inline-block; vertical-align: top; min-width: 12ch; ${from.tablet} { min-width: 16ch; } `; const ddCss = css` margin: 0; display: inline-block; vertical-align: top; `; return ( <> <ProgressIndicator steps={[ { title: 'Update' }, { title: 'Review' }, { title: 'Confirmation', isCurrentStep: true }, ]} additionalCSS={css` margin: ${space[5]}px 0 ${space[12]}px; `} /> <h2 css={css` border-top: 1px solid ${palette.neutral['86']}; ${headlineBold28}; ${until.tablet} { font-size: 1.25rem; line-height: 1.6; } `} > Delivery report confirmation </h2> <p css={css` ${textSans17}; `} > Your delivery problem report has been successfully submitted. </p> <span css={css` position: relative; display: block; margin: ${space[3]}px 0; padding: ${space[3]}px ${space[3]}px ${space[3]}px ${space[3] * 2 + 17}px; background-color: ${palette.neutral[97]}; ${textSans15}; ${from.tablet} { margin: ${space[5]}px 0; } `} > <i css={css` position: absolute; top: ${space[3]}px; left: ${space[3]}px; `} > <InfoIconDark fillColor={palette.brand[500]} /> </i> {deliveryProblemCredit?.showCredit ? `Thank you for reporting your delivery problem${ deliveryAddressContext.address && deliveryAddressContext.productsAffected && deliveryAddressContext.productsAffected?.length > 0 ? ' and updating your delivery details' : '' }. We will credit you for the affected issues and apologise for any inconvenience caused. We monitor these reports closely and use them to improve our service.` : `Your case is high priority. Our customer service team will try their best to contact you as soon as possible to resolve the issue.${ deliveryAddressContext.address && deliveryAddressContext.productsAffected && deliveryAddressContext.productsAffected?.length > 0 ? ' Thank you for updating your delivery details.' : '' }`} </span> <section css={css` border: 1px solid ${palette.neutral['86']}; margin-bottom: ${deliveryAddressContext.address && deliveryAddressContext.productsAffected && deliveryAddressContext.productsAffected?.length > 0 ? space[5] : space[9]}px; `} > <h2 css={css` margin: 0; padding: 14px ${space[3]}px; background-color: ${palette.neutral['97']}; border-bottom: 1px solid ${palette.neutral['86']}; ${textSansBold17}; ${from.tablet} { padding: 14px ${space[5]}px; } `} > Reported delivery problems </h2> <dl css={css` padding: 0 ${space[3]}px; ${textSans17}; display: flex; flex-wrap: wrap; flex-direction: column; justify-content: space-between; ${from.tablet} { flex-direction: initial; padding: 0 ${space[5]}px; } div { margin-top: 16px; ${from.tablet} { min-width: 50%; } } `} > <div> <dt css={dtCss}>Reference:</dt> <dd css={ddCss}>{problemReferenceId}</dd> </div> <div> <dt css={dtCss}>Date reported:</dt> <dd css={ddCss}> {dateString( new Date(), DATE_FNS_SHORT_OUTPUT_FORMAT, )} </dd> </div> <div> <dt css={dtCss}>Subscription ID:</dt> <dd css={ddCss} data-qm-masking="blocklist"> {props.subscriptionId} </dd> </div> <div> <dt css={dtCss}>Product:</dt> <dd css={ddCss}> {productType.shortFriendlyName || productType.friendlyName} </dd> </div> <div> <dt css={dtCss}>Contact number:</dt> <dd css={ddCss}> {Object.entries(props.data.contactPhoneNumbers) .filter( ([phoneType, phoneNumber]) => phoneType.toLowerCase() !== 'id' && phoneNumber, ) .map(([_, phoneNumber], index) => ( <span key={`phoneNo-${index}`} css={css` display: block; margin-bottom: ${space[3]}; `} > {phoneNumber} </span> )) || '-'} </dd> </div> <div> <dt css={dtCss}>Selected Issue(s):</dt> <dd css={ddCss}> {deliveryIssuePostPayload?.deliveryRecords?.length} </dd> </div> </dl> <div css={css` padding: 0 ${space[3]}px; margin-bottom: ${space[5]}px; ${from.tablet} { padding: 0 ${space[5]}px; } `} > {props.data.results.length ? ( filteredData.map( ( deliveryRecord: DeliveryRecordApiItem, listIndex, ) => ( <DeliveryRecordCard key={deliveryRecord.id} deliveryRecord={deliveryRecord} listIndex={listIndex} pageStatus={ PageStatus.ReportIssueConfirmation } showDeliveryInstructions={ productType.delivery?.records ?.showDeliveryInstructions } deliveryProblemMap={ props.data.deliveryProblemMap } recordCurrency={props.subscriptionCurrency} /> ), ) ) : ( <p>There aren't any delivery records to show you yet</p> )} </div> {deliveryProblemCredit?.showCredit && ( <dl css={css` ${textSans17}; padding: ${space[5]}px; margin: ${space[5]}px; background-color: ${palette.neutral['97']}; `} > <div css={css` display: inline-block; `} > <dt css={css` display: inline-block; font-weight: bold; `} > Credit amount: </dt> <dd css={css` display: inline-block; min-width: 9ch; `} > {deliveryProblemCredit.creditAmount} </dd> </div> <div css={css` display: inline-block; `} > <dt css={css` display: inline-block; font-weight: bold; `} > Credit date: </dt> <dd css={css` display: inline-block; `} > {deliveryProblemCredit.creditDate} </dd> </div> </dl> )} </section> {deliveryAddressContext.address && deliveryAddressContext.productsAffected && deliveryAddressContext.productsAffected?.length > 0 && ( <section css={css` border: 1px solid ${palette.neutral['86']}; margin-bottom: ${space[9]}px; `} > <h2 css={css` margin: 0; padding: 14px ${space[3]}px; background-color: ${palette.neutral['97']}; border-bottom: 1px solid ${palette.neutral['86']}; ${textSansBold17}; ${from.tablet} { padding: 14px ${space[5]}px; } `} > Delivery address changes </h2> <ReadOnlyAddressDisplay address={deliveryAddressContext.address} instructions={ (deliveryAddressContext.enableDeliveryInstructions && deliveryAddressContext.address .instructions) || undefined } /> <div css={css` padding: 0 ${space[3]}px; margin-top: ${space[5]}px; ${from.tablet} { padding: 0 ${space[5]}px; } `} > <p css={css` ${textSans17}; `} > Your change of address affects the following subscriptions: </p> <ProductDescriptionListTable content={ deliveryAddressContext.productsAffected } seperateEachRow /> </div> </section> )} <LinkButton cssOverrides={css` margin-top: ${space[3]}px; ${from.tablet} { margin-top: ${space[5]}px; } `} href={NAV_LINKS.accountOverview.link} > Return to your account </LinkButton> </> ); }; interface DeliveryRecordsConfirmationRouterState extends DeliveryRecordsRouterState { deliveryIssuePostPayload: DeliveryRecordsPostPayload; } export const DeliveryRecordsProblemConfirmation = () => { const location = useLocation(); const routerState = location.state as DeliveryRecordsConfirmationRouterState; if (!routerState) { return <Navigate to=".." />; } const { productDetail, deliveryIssuePostPayload } = routerState; const subscription = productDetail.subscription; const isTestUser = productDetail.isTestUser; return ( <DeliveryRecordsApiAsyncLoader render={renderDeliveryRecordsConfirmation(subscription)} fetch={createDeliveryRecordsProblemPost( subscription.subscriptionId, isTestUser, deliveryIssuePostPayload, )} loadingMessage={'Reporting problem...'} /> ); };