@@ -2,6 +2,7 @@ import { Button, Select, Typography } from '@linode/ui';
22import { capitalizeAllWords } from '@linode/utilities' ;
33import Grid from '@mui/material/Grid' ;
44import Paper from '@mui/material/Paper' ;
5+ import { Pagination } from 'akamai-cds-react-components/Pagination' ;
56import {
67 sortRows ,
78 Table ,
@@ -23,89 +24,100 @@ import {
2324 getFacadeRoleDescription ,
2425 mapEntityTypesForSelect ,
2526} from 'src/features/IAM/Shared/utilities' ;
27+ import { usePagination } from 'src/hooks/usePagination' ;
28+
29+ import { ROLES_TABLE_PREFERENCE_KEY } from '../../Shared/constants' ;
2630
2731import type { RoleView } from '../../Shared/types' ;
2832import type { SelectOption } from '@linode/ui' ;
2933import type { Order } from 'akamai-cds-react-components/Table' ;
30-
3134const ALL_ROLES_OPTION : SelectOption = {
3235 label : 'All Roles' ,
3336 value : 'all' ,
3437} ;
3538
3639interface Props {
37- roles : RoleView [ ] ;
40+ roles ? : RoleView [ ] ;
3841}
3942
40- export const RolesTable = ( { roles } : Props ) => {
41- const [ rows , setRows ] = useState ( roles ) ;
42-
43+ export const RolesTable = ( { roles = [ ] } : Props ) => {
4344 // Filter string for the search bar
4445 const [ filterString , setFilterString ] = React . useState ( '' ) ;
45-
46- // Get just the list of entity types from this list of roles, to be used in the selection filter
47- const filterableOptions = React . useMemo ( ( ) => {
48- return [ ALL_ROLES_OPTION , ...mapEntityTypesForSelect ( roles , ' Roles' ) ] ;
49- } , [ roles ] ) ;
50-
5146 const [ filterableEntityType , setFilterableEntityType ] =
5247 useState < null | SelectOption > ( ALL_ROLES_OPTION ) ;
53-
5448 const [ sort , setSort ] = useState <
5549 undefined | { column : string ; order : Order }
5650 > ( undefined ) ;
57-
5851 const [ selectedRows , setSelectedRows ] = useState < RoleView [ ] > ( [ ] ) ;
5952 const [ isDrawerOpen , setIsDrawerOpen ] = useState < boolean > ( false ) ;
6053
54+ const pagination = usePagination ( 1 , ROLES_TABLE_PREFERENCE_KEY ) ;
55+
56+ // Filtering
57+ const getFilteredRows = (
58+ text : string ,
59+ entityTypeVal = ALL_ROLES_OPTION . value
60+ ) => {
61+ return roles . filter (
62+ ( r ) =>
63+ ( entityTypeVal === ALL_ROLES_OPTION . value ||
64+ entityTypeVal === r . entity_type ) &&
65+ ( r . name . includes ( text ) ||
66+ r . description . includes ( text ) ||
67+ r . access . includes ( text ) )
68+ ) ;
69+ } ;
70+
71+ const filteredRows = React . useMemo (
72+ ( ) => getFilteredRows ( filterString , filterableEntityType ?. value ) ,
73+ [ roles , filterString , filterableEntityType ]
74+ ) ;
75+
76+ // Get just the list of entity types from this list of roles, to be used in the selection filter
77+ const filterableOptions = React . useMemo ( ( ) => {
78+ return [ ALL_ROLES_OPTION , ...mapEntityTypesForSelect ( roles , ' Roles' ) ] ;
79+ } , [ roles ] ) ;
80+
81+ const sortedRows = React . useMemo ( ( ) => {
82+ if ( ! sort ) return filteredRows ;
83+ return sortRows ( filteredRows , sort . order , sort . column ) ;
84+ } , [ filteredRows , sort ] ) ;
85+
86+ const paginatedRows = React . useMemo ( ( ) => {
87+ const start = ( pagination . page - 1 ) * pagination . pageSize ;
88+ return sortedRows . slice ( start , start + pagination . pageSize ) ;
89+ } , [ sortedRows , pagination . page , pagination . pageSize ] ) ;
90+
6191 const areAllSelected = React . useMemo ( ( ) => {
6292 return (
63- ! ! rows ?. length &&
93+ ! ! paginatedRows ?. length &&
6494 ! ! selectedRows ?. length &&
65- rows ?. length === selectedRows ?. length
95+ paginatedRows ?. length === selectedRows ?. length
6696 ) ;
67- } , [ rows , selectedRows ] ) ;
97+ } , [ paginatedRows , selectedRows ] ) ;
6898
6999 const handleSort = ( event : CustomEvent , column : string ) => {
70100 setSort ( { column, order : event . detail as Order } ) ;
71- const visibleRows = sortRows ( rows , event . detail as Order , column ) ;
72- setRows ( visibleRows ) ;
73101 } ;
74102
75103 const handleSelect = ( event : CustomEvent , row : 'all' | RoleView ) => {
76104 if ( row === 'all' ) {
77- setSelectedRows ( areAllSelected ? [ ] : rows ) ;
105+ setSelectedRows ( areAllSelected ? [ ] : paginatedRows ) ;
78106 } else if ( selectedRows . includes ( row ) ) {
79107 setSelectedRows ( selectedRows . filter ( ( r ) => r !== row ) ) ;
80108 } else {
81109 setSelectedRows ( [ ...selectedRows , row ] ) ;
82110 }
83111 } ;
84112
85- const getFilteredRows = (
86- text : string ,
87- entityTypeVal = ALL_ROLES_OPTION . value
88- ) => {
89- return roles . filter (
90- ( r ) =>
91- ( entityTypeVal === ALL_ROLES_OPTION . value ||
92- entityTypeVal === r . entity_type ) &&
93- ( r . name . includes ( text ) ||
94- r . description . includes ( text ) ||
95- r . access . includes ( text ) )
96- ) ;
97- } ;
98-
99113 const handleTextFilter = ( fs : string ) => {
100114 setFilterString ( fs ) ;
101- const filteredRows = getFilteredRows ( fs , filterableEntityType ?. value ) ;
102- setRows ( filteredRows ) ;
115+ pagination . handlePageChange ( 1 ) ;
103116 } ;
104117
105118 const handleChangeEntityTypeFilter = ( _ : never , entityType : SelectOption ) => {
106119 setFilterableEntityType ( entityType ?? ALL_ROLES_OPTION ) ;
107- const filteredRows = getFilteredRows ( filterString , entityType ?. value ) ;
108- setRows ( filteredRows ) ;
120+ pagination . handlePageChange ( 1 ) ;
109121 } ;
110122
111123 const assignRoleRow = ( row : RoleView ) => {
@@ -118,6 +130,15 @@ export const RolesTable = ({ roles }: Props) => {
118130 setIsDrawerOpen ( true ) ;
119131 } ;
120132
133+ const handlePageChange = ( event : CustomEvent < { page : number } > ) => {
134+ pagination . handlePageChange ( Number ( event . detail ) ) ;
135+ } ;
136+
137+ const handlePageSizeChange = ( event : CustomEvent < { pageSize : number } > ) => {
138+ const newSize = event . detail . pageSize ;
139+ pagination . handlePageSizeChange ( newSize ) ;
140+ pagination . handlePageChange ( 1 ) ;
141+ } ;
121142 return (
122143 < >
123144 < Paper sx = { ( theme ) => ( { marginTop : theme . tokens . spacing . S16 } ) } >
@@ -208,14 +229,14 @@ export const RolesTable = ({ roles }: Props) => {
208229 </ TableRow >
209230 </ TableHead >
210231 < TableBody >
211- { ! rows ?. length ? (
232+ { ! paginatedRows ?. length ? (
212233 < TableRow >
213234 < TableCell style = { { justifyContent : 'center' } } >
214235 No items to display.
215236 </ TableCell >
216237 </ TableRow >
217238 ) : (
218- rows . map ( ( roleRow ) => (
239+ paginatedRows . map ( ( roleRow ) => (
219240 < TableRow
220241 expandable
221242 hoverable
@@ -267,6 +288,14 @@ export const RolesTable = ({ roles }: Props) => {
267288 ) }
268289 </ TableBody >
269290 </ Table >
291+ < Pagination
292+ count = { filteredRows . length }
293+ onPageChange = { handlePageChange }
294+ onPageSizeChange = { handlePageSizeChange }
295+ page = { pagination . page }
296+ pageSize = { pagination . pageSize }
297+ style = { { borderTop : 0 } }
298+ />
270299 </ Paper >
271300 < AssignSelectedRolesDrawer
272301 onClose = { ( ) => setIsDrawerOpen ( false ) }
0 commit comments