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

feat: add new dates and plan price (#10774)

<img width="2159" height="1617" alt="image"
src="https://github.com/user-attachments/assets/478ea289-dc0f-439e-92e4-a22fa44a0650"
/>
This commit is contained in:
Jaanus Sellin 2025-10-10 13:44:58 +03:00 committed by GitHub
parent 8879cc4b46
commit 0d252558c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 91 additions and 10 deletions

View File

@ -5,6 +5,8 @@ import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstan
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { InstanceState } from 'interfaces/instance'; import { InstanceState } from 'interfaces/instance';
import { formatApiPath } from 'utils/formatPath'; import { formatApiPath } from 'utils/formatPath';
import { useDetailedInvoices } from 'hooks/api/getters/useDetailedInvoices/useDetailedInvoices';
import { formatCurrency } from '../BillingInvoices/BillingInvoice/formatCurrency.js';
const PORTAL_URL = formatApiPath('api/admin/invoices'); const PORTAL_URL = formatApiPath('api/admin/invoices');
type BillingInfoProps = {}; type BillingInfoProps = {};
@ -63,6 +65,7 @@ export const BillingInfo: FC<BillingInfoProps> = () => {
const { const {
uiConfig: { billing }, uiConfig: { billing },
} = useUiConfig(); } = useUiConfig();
const { planPrice, planCurrency } = useDetailedInvoices();
if (!instanceStatus) { if (!instanceStatus) {
return ( return (
@ -105,8 +108,13 @@ export const BillingInfo: FC<BillingInfoProps> = () => {
</StyledRow> </StyledRow>
<StyledRow> <StyledRow>
<StyledItemTitle>Plan price</StyledItemTitle>{' '} <StyledItemTitle>Plan price</StyledItemTitle>{' '}
{/* FIXME: where to take data from? */} <StyledItemValue>
<StyledItemValue>$450 / month</StyledItemValue> {planPrice !== undefined
? `${formatCurrency(planPrice, planCurrency)} ${
isPAYG ? 'per seat' : '/ month'
}`
: '-'}
</StyledItemValue>
</StyledRow> </StyledRow>
<StyledDivider /> <StyledDivider />
<StyledButton <StyledButton

View File

@ -5,7 +5,10 @@ import {
Accordion, Accordion,
AccordionSummary, AccordionSummary,
AccordionDetails, AccordionDetails,
Button,
} from '@mui/material'; } from '@mui/material';
import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined';
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { formatCurrency } from './formatCurrency.ts'; import { formatCurrency } from './formatCurrency.ts';
import { Badge } from 'component/common/Badge/Badge.tsx'; import { Badge } from 'component/common/Badge/Badge.tsx';
@ -84,6 +87,13 @@ const StyledTableRow = styled('div')(({ theme }) => ({
padding: theme.spacing(1, 0), padding: theme.spacing(1, 0),
})); }));
const CardActions = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'flex-end',
gap: theme.spacing(1),
padding: theme.spacing(1.5, 2, 2),
}));
export const BillingInvoice = ({ export const BillingInvoice = ({
status, status,
invoiceDate, invoiceDate,
@ -93,20 +103,20 @@ export const BillingInvoice = ({
mainLines, mainLines,
usageLines, usageLines,
}: DetailedInvoicesSchemaInvoicesItem) => { }: DetailedInvoicesSchemaInvoicesItem) => {
const title = invoiceDate const currency = mainLines[0]?.currency || usageLines?.[0]?.currency;
const formattedTitle = invoiceDate
? new Date(invoiceDate).toLocaleDateString(undefined, { ? new Date(invoiceDate).toLocaleDateString(undefined, {
month: 'long', month: 'long',
day: 'numeric', day: 'numeric',
}) })
: ''; : '';
const currency = mainLines[0]?.currency || usageLines?.[0]?.currency;
return ( return (
<CardLikeAccordion defaultExpanded> <CardLikeAccordion defaultExpanded>
<HeaderRoot <HeaderRoot
expandIcon={<ExpandMoreIcon />} expandIcon={<ExpandMoreIcon />}
id={`billing-invoice-${title}-header`} id={`billing-invoice-${formattedTitle}-header`}
> >
<HeaderLeft> <HeaderLeft>
<Typography <Typography
@ -114,7 +124,7 @@ export const BillingInvoice = ({
component='h3' component='h3'
sx={{ fontWeight: 700 }} sx={{ fontWeight: 700 }}
> >
{title} {formattedTitle}
</Typography> </Typography>
</HeaderLeft> </HeaderLeft>
<HeaderRight> <HeaderRight>
@ -170,6 +180,30 @@ export const BillingInvoice = ({
currency={currency} currency={currency}
/> />
</StyledInvoiceGrid> </StyledInvoiceGrid>
<CardActions>
{invoiceURL ? (
<Button
variant='outlined'
href={invoiceURL}
target='_blank'
rel='noreferrer'
startIcon={<ReceiptLongOutlinedIcon />}
>
View invoice
</Button>
) : null}
{invoicePDF ? (
<Button
variant='outlined'
href={invoicePDF}
target='_blank'
rel='noreferrer'
startIcon={<DownloadOutlinedIcon />}
>
Download PDF
</Button>
) : null}
</CardActions>
</AccordionDetails> </AccordionDetails>
</CardLikeAccordion> </CardLikeAccordion>
); );

View File

@ -1,7 +1,7 @@
import { formatLargeNumbers } from 'component/impact-metrics/metricsFormatters.ts'; import { formatLargeNumbers } from 'component/impact-metrics/metricsFormatters.ts';
import { formatCurrency } from '../formatCurrency.ts'; import { formatCurrency } from '../formatCurrency.ts';
import { ConsumptionIndicator } from '../ConsumptionIndicator/ConsumptionIndicator.tsx'; import { ConsumptionIndicator } from '../ConsumptionIndicator/ConsumptionIndicator.tsx';
import { styled } from '@mui/material'; import { styled, Typography } from '@mui/material';
import type { DetailedInvoicesLineSchema } from 'openapi'; import type { DetailedInvoicesLineSchema } from 'openapi';
import { StyledAmountCell } from '../BillingInvoice.styles.tsx'; import { StyledAmountCell } from '../BillingInvoice.styles.tsx';
@ -19,6 +19,17 @@ type BillingInvoiceRowProps = {
usage?: number; usage?: number;
}; };
const StyledDescriptionCell = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(0.5),
}));
const StyledSubText = styled(Typography)(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: theme.typography.body2.fontSize,
}));
export const BillingInvoiceRow = ({ export const BillingInvoiceRow = ({
quantity, quantity,
consumption, consumption,
@ -26,15 +37,37 @@ export const BillingInvoiceRow = ({
description, description,
currency, currency,
totalAmount, totalAmount,
startDate,
endDate,
}: DetailedInvoicesLineSchema) => { }: DetailedInvoicesLineSchema) => {
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))
: undefined; : undefined;
const formattedStart = startDate
? new Date(startDate).toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
})
: undefined;
const formattedEnd = endDate
? new Date(endDate).toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
})
: undefined;
return ( return (
<> <>
<div>{description}</div> <StyledDescriptionCell>
<div>{description}</div>
{formattedStart || formattedEnd ? (
<StyledSubText>
{formattedStart} - {formattedEnd}
</StyledSubText>
) : null}
</StyledDescriptionCell>
<StyledCellWithIndicator> <StyledCellWithIndicator>
<ConsumptionIndicator percentage={percentage || 0} /> <ConsumptionIndicator percentage={percentage || 0} />
{limit !== undefined ? formatLargeNumbers(limit) : ''} {limit !== undefined ? formatLargeNumbers(limit) : ''}

View File

@ -20,6 +20,8 @@ export const useDetailedInvoices = (options: SWRConfiguration = {}) => {
); );
const invoices = useMemo(() => data?.invoices ?? [], [data]); const invoices = useMemo(() => data?.invoices ?? [], [data]);
const planPrice = data?.planPrice;
const planCurrency = data?.planCurrency;
return { invoices, error, loading: isLoading }; return { invoices, planPrice, planCurrency, error, loading: isLoading };
}; };

View File

@ -11,4 +11,8 @@ import type { DetailedInvoicesSchemaInvoicesItem } from './detailedInvoicesSchem
export interface DetailedInvoicesSchema { export interface DetailedInvoicesSchema {
/** List of invoices with their line items */ /** List of invoices with their line items */
invoices: DetailedInvoicesSchemaInvoicesItem[]; invoices: DetailedInvoicesSchemaInvoicesItem[];
/** The currency code for the plan price */
planCurrency?: string;
/** The plan price in minor currency units */
planPrice?: number;
} }