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

View File

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

View File

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