diff --git a/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx b/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx index 2d9c1f1a3d..ff271e9a6e 100644 --- a/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx +++ b/frontend/src/component/admin/billing/BillingInfo/BillingInfo.tsx @@ -5,6 +5,8 @@ import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstan import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { InstanceState } from 'interfaces/instance'; 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'); type BillingInfoProps = {}; @@ -63,6 +65,7 @@ export const BillingInfo: FC = () => { const { uiConfig: { billing }, } = useUiConfig(); + const { planPrice, planCurrency } = useDetailedInvoices(); if (!instanceStatus) { return ( @@ -105,8 +108,13 @@ export const BillingInfo: FC = () => { Plan price{' '} - {/* FIXME: where to take data from? */} - $450 / month + + {planPrice !== undefined + ? `${formatCurrency(planPrice, planCurrency)} ${ + isPAYG ? 'per seat' : '/ month' + }` + : '-'} + ({ 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 = ({ status, invoiceDate, @@ -93,20 +103,20 @@ export const BillingInvoice = ({ mainLines, usageLines, }: DetailedInvoicesSchemaInvoicesItem) => { - const title = invoiceDate + const currency = mainLines[0]?.currency || usageLines?.[0]?.currency; + + const formattedTitle = invoiceDate ? new Date(invoiceDate).toLocaleDateString(undefined, { month: 'long', day: 'numeric', }) : ''; - const currency = mainLines[0]?.currency || usageLines?.[0]?.currency; - return ( } - id={`billing-invoice-${title}-header`} + id={`billing-invoice-${formattedTitle}-header`} > - {title} + {formattedTitle} @@ -170,6 +180,30 @@ export const BillingInvoice = ({ currency={currency} /> + + {invoiceURL ? ( + + ) : null} + {invoicePDF ? ( + + ) : null} + ); diff --git a/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceRow/BillingInvoiceRow.tsx b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceRow/BillingInvoiceRow.tsx index ea02adfb11..b620ea7df5 100644 --- a/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceRow/BillingInvoiceRow.tsx +++ b/frontend/src/component/admin/billing/BillingInvoices/BillingInvoice/BillingInvoiceRow/BillingInvoiceRow.tsx @@ -1,7 +1,7 @@ import { formatLargeNumbers } from 'component/impact-metrics/metricsFormatters.ts'; import { formatCurrency } from '../formatCurrency.ts'; import { ConsumptionIndicator } from '../ConsumptionIndicator/ConsumptionIndicator.tsx'; -import { styled } from '@mui/material'; +import { styled, Typography } from '@mui/material'; import type { DetailedInvoicesLineSchema } from 'openapi'; import { StyledAmountCell } from '../BillingInvoice.styles.tsx'; @@ -19,6 +19,17 @@ type BillingInvoiceRowProps = { 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 = ({ quantity, consumption, @@ -26,15 +37,37 @@ export const BillingInvoiceRow = ({ description, currency, totalAmount, + startDate, + endDate, }: DetailedInvoicesLineSchema) => { const percentage = limit && limit > 0 ? Math.min(100, Math.round(((consumption || 0) / limit) * 100)) : 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 ( <> -
{description}
+ +
{description}
+ {formattedStart || formattedEnd ? ( + + {formattedStart} - {formattedEnd} + + ) : null} +
{limit !== undefined ? formatLargeNumbers(limit) : '–'} diff --git a/frontend/src/hooks/api/getters/useDetailedInvoices/useDetailedInvoices.ts b/frontend/src/hooks/api/getters/useDetailedInvoices/useDetailedInvoices.ts index be6e53e34a..e27bf1d26e 100644 --- a/frontend/src/hooks/api/getters/useDetailedInvoices/useDetailedInvoices.ts +++ b/frontend/src/hooks/api/getters/useDetailedInvoices/useDetailedInvoices.ts @@ -20,6 +20,8 @@ export const useDetailedInvoices = (options: SWRConfiguration = {}) => { ); 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 }; }; diff --git a/frontend/src/openapi/models/detailedInvoicesSchema.ts b/frontend/src/openapi/models/detailedInvoicesSchema.ts index d5bc0e413c..ed58442969 100644 --- a/frontend/src/openapi/models/detailedInvoicesSchema.ts +++ b/frontend/src/openapi/models/detailedInvoicesSchema.ts @@ -11,4 +11,8 @@ import type { DetailedInvoicesSchemaInvoicesItem } from './detailedInvoicesSchem export interface DetailedInvoicesSchema { /** List of invoices with their line items */ invoices: DetailedInvoicesSchemaInvoicesItem[]; + /** The currency code for the plan price */ + planCurrency?: string; + /** The plan price in minor currency units */ + planPrice?: number; }