in client/components/mma/billing/Billing.tsx [138:313]
function renderProductBillingInfo([productGrouping, productDetails]: [
string,
ProductDetailWithInvoice[],
]) {
return (
productDetails.length > 0 && (
<Fragment key={productGrouping}>
{productDetails.map((productDetail) => {
const mainPlan = getMainPlan(productDetail.subscription);
if (!mainPlan) {
throw new Error(
'mainPlan does not exist for product in billing page',
);
}
const specificProductType = getSpecificProductType(
productDetail.tier,
);
const groupedProductType =
GROUPED_PRODUCT_TYPES[
specificProductType.groupedProductType
];
const hasCancellationPending =
productDetail.subscription.cancelledAt;
const cancelledCopy =
specificProductType.cancelledCopy ||
groupedProductType.cancelledCopy;
const nextPaymentDetails = getNextPaymentDetails(
mainPlan,
productDetail.subscription,
null,
!!productDetail.alertText,
);
const paidPlan = getMainPlan(
productDetail.subscription,
) as PaidSubscriptionPlan;
const maybePatronSuffix =
productDetail.subscription.readerType === 'Patron'
? ' - Patron'
: '';
const productInvoiceData = productDetail.invoices.map(
(invoice) => ({
...invoice,
pdfPath: `/api/${invoice.pdfPath}`,
currency: paidPlan.currency,
currencyISO: paidPlan.currencyISO,
productUrlPart: specificProductType.urlPart,
}),
);
const resultsPerPage = paidPlan.billingPeriod?.includes(
'year',
)
? productInvoiceData.length
: 6;
return (
<Fragment
key={productDetail.subscription.subscriptionId}
>
<div
css={css`
${subHeadingBorderTopCss}
display: flex;
align-items: start;
justify-content: space-between;
`}
>
<h2
css={css`
${subHeadingTitleCss}
margin: 0;
`}
>
{specificProductType.productTitle(mainPlan)}
{maybePatronSuffix}
</h2>
{isGift(productDetail.subscription) && (
<i
css={css`
margin: 4px 0 0 ${space[3]}px;
`}
>
<GiftIcon
alignArrowToThisSide={'left'}
/>
</i>
)}
</div>
{hasCancellationPending && (
<p
css={css`
${textSans17};
`}
>
<ErrorIcon fill={palette.brandAlt[200]} />
<span
css={css`
margin-left: ${space[2]}px;
`}
>
{cancelledCopy}{' '}
<strong>
{parseDate(
productDetail.subscription.end,
).dateStr()}
</strong>
</span>
.
</p>
)}
<BasicProductInfoTable
groupedProductType={groupedProductType}
productDetail={productDetail}
/>
<SixForSixExplainerIfApplicable
additionalCss={css`
${textSans17};
`}
mainPlan={mainPlan}
hasCancellationPending={hasCancellationPending}
/>
<PaymentDetailsTable
productDetail={productDetail}
nextPaymentDetails={nextPaymentDetails}
hasCancellationPending={hasCancellationPending}
tableHeading="Payment"
/>
{productDetail.isPaidTier &&
productDetail.subscription
.safeToUpdatePaymentMethod && (
<LinkButton
colour={
productDetail.alertText
? palette.brand[400]
: palette.brand[800]
}
textColour={
productDetail.alertText
? palette.neutral[100]
: palette.brand[400]
}
fontWeight={'bold'}
alert={!!productDetail.alertText}
text="Update payment method"
ariaLabelText={`${specificProductType.productTitle(
mainPlan,
)} : Update payment method`}
to={`/payment/${specificProductType.urlPart}`}
state={{
productDetail,
flowReferrer: {
title: NAV_LINKS.billing.title,
link: NAV_LINKS.billing.link,
},
}}
/>
)}
{productInvoiceData.length > 0 && (
<div
css={css`
margin-top: ${space[12]}px;
margin-bottom: ${space[3]}px;
`}
>
<InvoicesTable
resultsPerPage={resultsPerPage}
invoiceData={productInvoiceData}
/>
</div>
)}
</Fragment>
);
})}
</Fragment>
)
);
}