1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00

feat: enhance billing invoice components (#10799)

## About the changes
- Hide "included usage" column if an invoice doesn't have any items that
have lines with `limit`
- Minor component types refactors
- Only open (extend accordion) for the first invoice on the list
This commit is contained in:
Tymoteusz Czech 2025-10-15 09:00:59 +02:00 committed by GitHub
parent f9ed38ca98
commit 38bb9b3bfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 25 deletions

View File

@ -1,4 +1,4 @@
import type { FC, ReactNode } from 'react'; import type { ComponentProps, FC, ReactNode } from 'react';
import { import {
Typography, Typography,
styled, styled,
@ -17,7 +17,7 @@ import { BillingInvoiceFooter } from './BillingInvoiceFooter/BillingInvoiceFoote
import { StyledAmountCell, StyledSubgrid } from './BillingInvoice.styles.tsx'; import { StyledAmountCell, StyledSubgrid } from './BillingInvoice.styles.tsx';
import type { DetailedInvoicesSchemaInvoicesItem } from 'openapi'; import type { DetailedInvoicesSchemaInvoicesItem } from 'openapi';
const CardLikeAccordion = styled(Accordion)(({ theme }) => ({ const StyledAccordion = styled(Accordion)(({ theme }) => ({
background: theme.palette.background.paper, background: theme.palette.background.paper,
borderRadius: theme.shape.borderRadiusLarge, borderRadius: theme.shape.borderRadiusLarge,
boxShadow: theme.boxShadows.card, boxShadow: theme.boxShadows.card,
@ -94,6 +94,9 @@ const CardActions = styled('div')(({ theme }) => ({
padding: theme.spacing(1.5, 2, 2), padding: theme.spacing(1.5, 2, 2),
})); }));
type BillingInvoiceProps = DetailedInvoicesSchemaInvoicesItem &
Pick<ComponentProps<typeof Accordion>, 'defaultExpanded'>;
export const BillingInvoice = ({ export const BillingInvoice = ({
status, status,
invoiceDate, invoiceDate,
@ -102,8 +105,9 @@ export const BillingInvoice = ({
totalAmount, totalAmount,
mainLines, mainLines,
usageLines, usageLines,
}: DetailedInvoicesSchemaInvoicesItem) => { defaultExpanded,
const currency = mainLines[0]?.currency || usageLines?.[0]?.currency; }: BillingInvoiceProps) => {
const currency = mainLines?.[0]?.currency || usageLines?.[0]?.currency;
const formattedTitle = invoiceDate const formattedTitle = invoiceDate
? new Date(invoiceDate).toLocaleDateString(undefined, { ? new Date(invoiceDate).toLocaleDateString(undefined, {
@ -112,8 +116,12 @@ export const BillingInvoice = ({
}) })
: ''; : '';
const hasLimitsColumn =
mainLines?.some((line) => line.limit) ||
usageLines?.some((line) => line.limit);
return ( return (
<CardLikeAccordion defaultExpanded> <StyledAccordion defaultExpanded={Boolean(defaultExpanded)}>
<HeaderRoot <HeaderRoot
expandIcon={<ExpandMoreIcon />} expandIcon={<ExpandMoreIcon />}
id={`billing-invoice-${formattedTitle}-header`} id={`billing-invoice-${formattedTitle}-header`}
@ -151,7 +159,11 @@ export const BillingInvoice = ({
<StyledInvoiceGrid> <StyledInvoiceGrid>
<StyledSubgrid> <StyledSubgrid>
<HeaderCell>Description</HeaderCell> <HeaderCell>Description</HeaderCell>
{hasLimitsColumn ? (
<HeaderCell>Included</HeaderCell> <HeaderCell>Included</HeaderCell>
) : (
<HeaderCell />
)}
<HeaderCell>Quantity</HeaderCell> <HeaderCell>Quantity</HeaderCell>
<HeaderCell> <HeaderCell>
<StyledAmountCell>Amount</StyledAmountCell> <StyledAmountCell>Amount</StyledAmountCell>
@ -160,7 +172,10 @@ export const BillingInvoice = ({
{mainLines.map((line) => ( {mainLines.map((line) => (
<TableBody key={line.description}> <TableBody key={line.description}>
<StyledTableRow key={line.description}> <StyledTableRow key={line.description}>
<BillingInvoiceRow {...line} /> <BillingInvoiceRow
{...line}
showLimits={hasLimitsColumn}
/>
</StyledTableRow> </StyledTableRow>
</TableBody> </TableBody>
))} ))}
@ -169,7 +184,10 @@ export const BillingInvoice = ({
<StyledSectionTitle>Usage</StyledSectionTitle> <StyledSectionTitle>Usage</StyledSectionTitle>
{usageLines.map((line) => ( {usageLines.map((line) => (
<StyledTableRow key={line.description}> <StyledTableRow key={line.description}>
<BillingInvoiceRow {...line} /> <BillingInvoiceRow
{...line}
showLimits={hasLimitsColumn}
/>
</StyledTableRow> </StyledTableRow>
))} ))}
</TableBody> </TableBody>
@ -205,6 +223,6 @@ export const BillingInvoice = ({
) : null} ) : null}
</CardActions> </CardActions>
</AccordionDetails> </AccordionDetails>
</CardLikeAccordion> </StyledAccordion>
); );
}; };

View File

@ -11,14 +11,6 @@ const StyledCellWithIndicator = styled('div')(({ theme }) => ({
gap: theme.spacing(1), gap: theme.spacing(1),
})); }));
type BillingInvoiceRowProps = {
description: string;
quantity?: number;
amount?: number;
quota?: number;
usage?: number;
};
const StyledDescriptionCell = styled('div')(({ theme }) => ({ const StyledDescriptionCell = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -30,6 +22,10 @@ const StyledSubText = styled(Typography)(({ theme }) => ({
fontSize: theme.typography.body2.fontSize, fontSize: theme.typography.body2.fontSize,
})); }));
type BillingInvoiceRowProps = DetailedInvoicesLineSchema & {
showLimits?: boolean;
};
export const BillingInvoiceRow = ({ export const BillingInvoiceRow = ({
quantity, quantity,
consumption, consumption,
@ -39,7 +35,8 @@ export const BillingInvoiceRow = ({
totalAmount, totalAmount,
startDate, startDate,
endDate, endDate,
}: DetailedInvoicesLineSchema) => { showLimits,
}: BillingInvoiceRowProps) => {
const percentage = const percentage =
limit && limit > 0 limit && limit > 0
? Math.min(100, Math.round(((consumption || 0) / limit) * 100)) ? Math.min(100, Math.round(((consumption || 0) / limit) * 100))
@ -68,11 +65,15 @@ export const BillingInvoiceRow = ({
</StyledSubText> </StyledSubText>
) : null} ) : null}
</StyledDescriptionCell> </StyledDescriptionCell>
{showLimits ? (
<StyledCellWithIndicator> <StyledCellWithIndicator>
<ConsumptionIndicator percentage={percentage || 0} /> <ConsumptionIndicator percentage={percentage || 0} />
{limit !== undefined ? formatLargeNumbers(limit) : ''} {limit !== undefined ? formatLargeNumbers(limit) : ''}
{percentage !== undefined ? ` (${percentage}%)` : ''} {percentage !== undefined ? ` (${percentage}%)` : ''}
</StyledCellWithIndicator> </StyledCellWithIndicator>
) : (
<StyledCellWithIndicator />
)}
<div>{quantity ? formatLargeNumbers(quantity) : ''}</div> <div>{quantity ? formatLargeNumbers(quantity) : ''}</div>
<StyledAmountCell> <StyledAmountCell>
{formatCurrency(totalAmount || 0, currency)} {formatCurrency(totalAmount || 0, currency)}

View File

@ -14,16 +14,17 @@ export const BillingInvoices: FC = () => {
const { invoices, loading } = useDetailedInvoices(); const { invoices, loading } = useDetailedInvoices();
if (loading) { if (loading) {
return null; return <StyledContainer />;
} }
return ( return (
<StyledContainer> <StyledContainer>
{invoices.length > 0 ? ( {invoices.length > 0 ? (
<> <>
{invoices.map((invoice) => ( {invoices.map((invoice, index) => (
<BillingInvoice <BillingInvoice
key={invoice.invoiceDate} key={invoice.invoiceDate}
defaultExpanded={index === 0}
{...invoice} {...invoice}
/> />
))} ))}