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:
		
							parent
							
								
									8879cc4b46
								
							
						
					
					
						commit
						0d252558c4
					
				| @ -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<BillingInfoProps> = () => { | ||||
|     const { | ||||
|         uiConfig: { billing }, | ||||
|     } = useUiConfig(); | ||||
|     const { planPrice, planCurrency } = useDetailedInvoices(); | ||||
| 
 | ||||
|     if (!instanceStatus) { | ||||
|         return ( | ||||
| @ -105,8 +108,13 @@ export const BillingInfo: FC<BillingInfoProps> = () => { | ||||
|             </StyledRow> | ||||
|             <StyledRow> | ||||
|                 <StyledItemTitle>Plan price</StyledItemTitle>{' '} | ||||
|                 {/* FIXME: where to take data from? */} | ||||
|                 <StyledItemValue>$450 / month</StyledItemValue> | ||||
|                 <StyledItemValue> | ||||
|                     {planPrice !== undefined | ||||
|                         ? `${formatCurrency(planPrice, planCurrency)} ${ | ||||
|                               isPAYG ? 'per seat' : '/ month' | ||||
|                           }` | ||||
|                         : '-'} | ||||
|                 </StyledItemValue> | ||||
|             </StyledRow> | ||||
|             <StyledDivider /> | ||||
|             <StyledButton | ||||
|  | ||||
| @ -5,7 +5,10 @@ import { | ||||
|     Accordion, | ||||
|     AccordionSummary, | ||||
|     AccordionDetails, | ||||
|     Button, | ||||
| } 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 { formatCurrency } from './formatCurrency.ts'; | ||||
| import { Badge } from 'component/common/Badge/Badge.tsx'; | ||||
| @ -84,6 +87,13 @@ const StyledTableRow = styled('div')(({ theme }) => ({ | ||||
|     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 ( | ||||
|         <CardLikeAccordion defaultExpanded> | ||||
|             <HeaderRoot | ||||
|                 expandIcon={<ExpandMoreIcon />} | ||||
|                 id={`billing-invoice-${title}-header`} | ||||
|                 id={`billing-invoice-${formattedTitle}-header`} | ||||
|             > | ||||
|                 <HeaderLeft> | ||||
|                     <Typography | ||||
| @ -114,7 +124,7 @@ export const BillingInvoice = ({ | ||||
|                         component='h3' | ||||
|                         sx={{ fontWeight: 700 }} | ||||
|                     > | ||||
|                         {title} | ||||
|                         {formattedTitle} | ||||
|                     </Typography> | ||||
|                 </HeaderLeft> | ||||
|                 <HeaderRight> | ||||
| @ -170,6 +180,30 @@ export const BillingInvoice = ({ | ||||
|                         currency={currency} | ||||
|                     /> | ||||
|                 </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> | ||||
|         </CardLikeAccordion> | ||||
|     ); | ||||
|  | ||||
| @ -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 ( | ||||
|         <> | ||||
|             <div>{description}</div> | ||||
|             <StyledDescriptionCell> | ||||
|                 <div>{description}</div> | ||||
|                 {formattedStart || formattedEnd ? ( | ||||
|                     <StyledSubText> | ||||
|                         {formattedStart} - {formattedEnd} | ||||
|                     </StyledSubText> | ||||
|                 ) : null} | ||||
|             </StyledDescriptionCell> | ||||
|             <StyledCellWithIndicator> | ||||
|                 <ConsumptionIndicator percentage={percentage || 0} /> | ||||
|                 {limit !== undefined ? formatLargeNumbers(limit) : '–'} | ||||
|  | ||||
| @ -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 }; | ||||
| }; | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user