11/**
22 * Permission groups page.
33 */
4- import { FC , useContext , useState } from 'react'
4+ import { ChangeEvent , FC , useContext , useMemo , useState } from 'react'
55import classNames from 'classnames'
66
7- import { Button , LoadingSpinner , PageTitle } from '~/libs/ui'
7+ import { Button , InputText , LoadingSpinner , PageTitle } from '~/libs/ui'
88import { PlusIcon } from '@heroicons/react/solid'
99
1010import { DialogAddGroup } from '../../lib/components/DialogAddGroup'
@@ -24,6 +24,7 @@ const pageTitle = 'Groups'
2424
2525export const PermissionGroupsPage : FC < Props > = ( props : Props ) => {
2626 const [ openDialogAddGroup , setOpenDialogAddGroup ] = useState ( false )
27+ const [ searchTerm , setSearchTerm ] = useState ( '' )
2728 const { loadUser, cancelLoadUser, usersMapping } : AdminAppContextType
2829 = useContext ( AdminAppContext )
2930 const {
@@ -37,6 +38,26 @@ export const PermissionGroupsPage: FC<Props> = (props: Props) => {
3738 usersMapping ,
3839 )
3940
41+ const filteredGroups = useMemo ( ( ) => {
42+ const normalized = searchTerm
43+ . trim ( )
44+ . toLowerCase ( )
45+ if ( ! normalized ) {
46+ return groups
47+ }
48+
49+ return groups . filter ( group => {
50+ const id = group . id ? group . id . toLowerCase ( ) : ''
51+ const name = group . name ? group . name . toLowerCase ( ) : ''
52+
53+ return id . includes ( normalized ) || name . includes ( normalized )
54+ } )
55+ } , [ groups , searchTerm ] )
56+ const hasSearchTerm = useMemo (
57+ ( ) => searchTerm . trim ( ) . length > 0 ,
58+ [ searchTerm ] ,
59+ )
60+
4061 return (
4162 < div className = { classNames ( styles . container , props . className ) } >
4263 < PageTitle > { pageTitle } </ PageTitle >
@@ -51,24 +72,40 @@ export const PermissionGroupsPage: FC<Props> = (props: Props) => {
5172 </ div >
5273 ) : (
5374 < >
54- < Button
55- primary
56- size = 'lg'
57- icon = { PlusIcon }
58- iconToLeft
59- label = 'new group'
60- onClick = { function onClick ( ) {
61- setOpenDialogAddGroup ( true )
62- } }
63- className = { styles . btnNewGroup }
64- />
65- { groups . length === 0 ? (
75+ < div className = { styles . actions } >
76+ < InputText
77+ name = 'groupSearch'
78+ type = 'text'
79+ label = 'Search groups'
80+ placeholder = 'Search by name or ID'
81+ value = { searchTerm }
82+ onChange = { function onChange ( event : ChangeEvent < HTMLInputElement > ) {
83+ setSearchTerm ( event . target . value )
84+ } }
85+ forceUpdateValue
86+ classNameWrapper = { styles . searchField }
87+ />
88+ < Button
89+ primary
90+ size = 'lg'
91+ icon = { PlusIcon }
92+ iconToLeft
93+ label = 'new group'
94+ onClick = { function onClick ( ) {
95+ setOpenDialogAddGroup ( true )
96+ } }
97+ className = { styles . btnNewGroup }
98+ />
99+ </ div >
100+ { filteredGroups . length === 0 ? (
66101 < p className = { styles . noRecordFound } >
67- { MSG_NO_RECORD_FOUND }
102+ { hasSearchTerm
103+ ? 'No groups match your search.'
104+ : MSG_NO_RECORD_FOUND }
68105 </ p >
69106 ) : (
70107 < GroupsTable
71- datas = { groups }
108+ datas = { filteredGroups }
72109 usersMapping = { usersMapping }
73110 />
74111 ) }
0 commit comments