66 useProfile ,
77 useRegionsQuery ,
88} from '@linode/queries' ;
9- import { Autocomplete , Typography } from '@linode/ui' ;
9+ import { Autocomplete , Notice , Typography , WarningIcon } from '@linode/ui' ;
1010import { getAll , useSet } from '@linode/utilities' ;
1111import Grid from '@mui/material/Grid' ;
1212import Paper from '@mui/material/Paper' ;
@@ -37,6 +37,7 @@ import {
3737 printInvoice ,
3838 printPayment ,
3939} from 'src/features/Billing/PdfGenerator/PdfGenerator' ;
40+ import { usePermissions } from 'src/features/IAM/hooks/usePermissions' ;
4041import { useFlags } from 'src/hooks/useFlags' ;
4142import { useOrderV2 } from 'src/hooks/useOrderV2' ;
4243import { usePaginationV2 } from 'src/hooks/usePaginationV2' ;
@@ -199,6 +200,16 @@ export const BillingActivityPanel = React.memo((props: Props) => {
199200 preferenceKey : 'billing-activity-order' ,
200201 } ) ;
201202
203+ const { data : permissions } = usePermissions ( 'account' , [
204+ 'list_billing_payments' ,
205+ 'list_billing_invoices' ,
206+ 'list_invoice_items' ,
207+ ] ) ;
208+
209+ const canViewInvoices = permissions . list_billing_invoices ;
210+ const canViewPayments = permissions . list_billing_payments ;
211+ const canViewInvoiceDetails = permissions . list_invoice_items ;
212+
202213 const isAkamaiCustomer = account ?. billing_source === 'akamai' ;
203214 const { classes } = useStyles ( ) ;
204215 const flags = useFlags ( ) ;
@@ -218,13 +229,13 @@ export const BillingActivityPanel = React.memo((props: Props) => {
218229 data : payments ,
219230 error : accountPaymentsError ,
220231 isLoading : accountPaymentsLoading ,
221- } = useAllAccountPayments ( { } , filter ) ;
232+ } = useAllAccountPayments ( { } , filter , canViewPayments ) ;
222233
223234 const {
224235 data : invoices ,
225236 error : accountInvoicesError ,
226237 isLoading : accountInvoicesLoading ,
227- } = useAllAccountInvoices ( { } , filter ) ;
238+ } = useAllAccountInvoices ( { } , filter , canViewInvoices ) ;
228239
229240 const downloadInvoicePDF = React . useCallback (
230241 ( invoiceId : number ) => {
@@ -356,7 +367,19 @@ export const BillingActivityPanel = React.memo((props: Props) => {
356367 return (
357368 < TableRowEmpty
358369 colSpan = { NUM_COLS }
359- message = "No Billing & Payment History found."
370+ message = {
371+ canViewInvoices && canViewPayments ? (
372+ 'No Billing & Payment History found.'
373+ ) : (
374+ < >
375+ < WarningIcon
376+ style = { { position : 'relative' , top : 4 , marginRight : 2 } }
377+ width = { 16 }
378+ /> { ' ' }
379+ You do not have permission to view billing or payment history.
380+ </ >
381+ )
382+ }
360383 />
361384 ) ;
362385 }
@@ -365,6 +388,7 @@ export const BillingActivityPanel = React.memo((props: Props) => {
365388 const lastItem = idx === orderedPaginatedData . length - 1 ;
366389 return (
367390 < ActivityFeedItem
391+ canViewInvoiceDetails = { canViewInvoiceDetails }
368392 downloadPDF = {
369393 thisItem . type === 'invoice'
370394 ? downloadInvoicePDF
@@ -428,6 +452,16 @@ export const BillingActivityPanel = React.memo((props: Props) => {
428452 < Autocomplete
429453 className = { classes . transactionType }
430454 disableClearable
455+ disabled = { ! canViewInvoices && ! canViewPayments }
456+ filterOptions = { ( options ) => {
457+ if ( ! canViewInvoices ) {
458+ return options . filter ( ( option ) => option . value !== 'invoice' ) ;
459+ }
460+ if ( ! canViewPayments ) {
461+ return options . filter ( ( option ) => option . value !== 'payment' ) ;
462+ }
463+ return options ;
464+ } }
431465 label = "Transaction Types"
432466 noMarginTop
433467 onChange = { ( _ , item ) => {
@@ -457,6 +491,16 @@ export const BillingActivityPanel = React.memo((props: Props) => {
457491 />
458492 </ div >
459493 </ StyledBillingAndPaymentHistoryHeader >
494+ { ( canViewInvoices && ! canViewPayments ) ||
495+ ( ! canViewInvoices && canViewPayments ) ? (
496+ < Notice
497+ spacingBottom = { 20 }
498+ text = { `You do not have permission to view ${
499+ canViewInvoices ? 'payments' : 'invoices'
500+ } history.`}
501+ variant = "error"
502+ />
503+ ) : null }
460504 < Table aria-label = "List of Invoices and Payments" sx = { { border : 0 } } >
461505 < TableHead >
462506 < TableRow >
@@ -473,8 +517,9 @@ export const BillingActivityPanel = React.memo((props: Props) => {
473517 >
474518 Amount
475519 </ TableSortCell >
476-
477- < TableCell className = { classes . pdfDownloadColumn } />
520+ { canViewInvoiceDetails && (
521+ < TableCell className = { classes . pdfDownloadColumn } />
522+ ) }
478523 </ TableRow >
479524 </ TableHead >
480525 < TableBody > { renderTableContent ( ) } </ TableBody >
@@ -503,6 +548,7 @@ const StyledBillingAndPaymentHistoryHeader = styled('div', {
503548// <ActivityFeedItem />
504549// =============================================================================
505550interface ActivityFeedItemProps extends ActivityFeedItem {
551+ canViewInvoiceDetails : boolean ;
506552 downloadPDF : ( id : number ) => void ;
507553 hasError : boolean ;
508554 isLoading : boolean ;
@@ -515,6 +561,7 @@ export const ActivityFeedItem = React.memo((props: ActivityFeedItemProps) => {
515561 const { iamRbacPrimaryNavChanges } = useFlags ( ) ;
516562
517563 const {
564+ canViewInvoiceDetails,
518565 date,
519566 downloadPDF,
520567 hasError,
@@ -544,7 +591,7 @@ export const ActivityFeedItem = React.memo((props: ActivityFeedItemProps) => {
544591 return (
545592 < TableRow data-testid = { `${ type } -${ id } ` } sx = { sxRow } >
546593 < TableCell >
547- { type === 'invoice' ? (
594+ { type === 'invoice' && canViewInvoiceDetails ? (
548595 < Link
549596 to = {
550597 iamRbacPrimaryNavChanges
@@ -564,14 +611,16 @@ export const ActivityFeedItem = React.memo((props: ActivityFeedItemProps) => {
564611 < TableCell className = { classes . totalColumn } >
565612 < Currency quantity = { total } wrapInParentheses = { total < 0 } />
566613 </ TableCell >
567- < TableCell className = { classes . pdfDownloadColumn } >
568- < InlineMenuAction
569- actionText = { action . title }
570- className = { action . className }
571- loading = { isLoading }
572- onClick = { action . onClick }
573- />
574- </ TableCell >
614+ { canViewInvoiceDetails && (
615+ < TableCell className = { classes . pdfDownloadColumn } >
616+ < InlineMenuAction
617+ actionText = { action . title }
618+ className = { action . className }
619+ loading = { isLoading }
620+ onClick = { action . onClick }
621+ />
622+ </ TableCell >
623+ ) }
575624 </ TableRow >
576625 ) ;
577626} ) ;
0 commit comments