@@ -14,13 +14,11 @@ export default function Overview() {
1414 const { user } = useSCE ( ) ;
1515 const [ toggleDelete , setToggleDelete ] = useState ( false ) ;
1616 const [ loading , setLoading ] = useState ( false ) ;
17- const [ paginationText , setPaginationText ] = useState ( '' ) ;
18- const [ users , setUsers ] = useState ( [ ] ) ;
17+ const [ allUsers , setAllUsers ] = useState ( [ ] ) ;
18+ const [ filteredUsers , setFilteredUsers ] = useState ( [ ] ) ;
1919 const [ page , setPage ] = useState ( 0 ) ;
2020 const [ total , setTotal ] = useState ( 0 ) ;
2121 const [ userToDelete , setUserToDelete ] = useState ( { } ) ;
22- const [ queryResult , setQueryResult ] = useState ( [ ] ) ;
23- const [ rowsPerPage , setRowsPerPage ] = useState ( 0 ) ;
2422 const [ query , setQuery ] = useState ( '' ) ;
2523 const [ currentSortColumn , setCurrentSortColumn ] = useState ( 'joinDate' ) ;
2624 const [ currentSortOrder , setCurrentSortOrder ] = useState ( 'desc' ) ;
@@ -36,24 +34,18 @@ export default function Overview() {
3634 ) ;
3735 if ( response . error ) {
3836 alert ( 'unable to delete user, check logs' ) ;
37+ return ;
3938 }
4039 if ( userToDel . _id === user . _id ) {
4140 // logout
4241 window . localStorage . removeItem ( 'jwtToken' ) ;
4342 window . location . reload ( ) ;
4443 return window . alert ( 'Self-deprecation is an art' ) ;
4544 }
46- setUsers (
47- users . filter (
48- child => ! child . _id . includes ( userToDel . _id )
49- )
50- ) ;
51- setTotal ( total - 1 ) ;
52- setQueryResult (
53- queryResult . filter (
54- child => ! child . _id . includes ( userToDel . _id )
55- )
56- ) ;
45+
46+ // Refetch all users after deletion
47+ await callDatabase ( ) ;
48+ // The filtering useEffect will automatically reapply current search
5749 }
5850
5951 function mark ( bool ) {
@@ -62,19 +54,16 @@ export default function Overview() {
6254
6355 async function callDatabase ( ) {
6456 setLoading ( true ) ;
65- const sortColumn = currentSortOrder === 'none' ? 'joinDate' : currentSortColumn ;
66- const sortOrder = currentSortOrder === 'none' ? 'desc' : currentSortOrder ;
6757 const apiResponse = await getAllUsers ( {
6858 token : user . token ,
69- query : query ,
70- page : page ,
71- sortColumn : sortColumn ,
72- sortOrder : sortOrder
59+ query : '' , // Filter on client, not server
60+ page : - 1 , // Special value to fetch all users
61+ sortColumn : 'joinDate' ,
62+ sortOrder : 'desc'
7363 } ) ;
7464 if ( ! apiResponse . error ) {
75- setUsers ( apiResponse . responseData . items ) ;
65+ setAllUsers ( apiResponse . responseData . items ) ;
7666 setTotal ( apiResponse . responseData . total ) ;
77- setRowsPerPage ( apiResponse . responseData . rowsPerPage ) ;
7867 }
7968 setLoading ( false ) ;
8069 }
@@ -89,25 +78,61 @@ export default function Overview() {
8978 useEffect ( ( ) => {
9079 callDatabase ( ) ;
9180 getClubRevenueData ( ) ;
92- } , [ page , currentSortColumn , currentSortOrder ] ) ;
81+ } , [ ] ) ;
9382
83+ // Client-side filtering and sorting
9484 useEffect ( ( ) => {
85+ if ( ! allUsers . length ) {
86+ setFilteredUsers ( [ ] ) ;
87+ return ;
88+ }
9589
96- const amountOfUsersOnCurrentPage = Math . min ( ( page + 1 ) * rowsPerPage , users . length ) ;
97- const pageOffset = page * rowsPerPage ;
98- const startingElementNumber = ( page * rowsPerPage ) + 1 ;
99- const endingElementNumber = amountOfUsersOnCurrentPage + pageOffset ;
100- setPaginationText (
101- < >
102- < p className = 'md:hidden text-gray-700 dark:text-white' >
103- { startingElementNumber } - { endingElementNumber } / { total }
104- </ p >
105- < p className = "hidden md:inline-block text-gray-700 dark:text-white" >
106- Showing < span className = 'font-medium' > { startingElementNumber } </ span > to < span className = 'font-medium' > { endingElementNumber } </ span > of < span className = 'font-medium' > { total } </ span > results
107- </ p >
108- </ >
109- ) ;
110- } , [ page , rowsPerPage , users , total ] ) ;
90+ // Filter users based on query
91+ let filtered = allUsers ;
92+ if ( query . trim ( ) ) {
93+ const searchTerm = query . trim ( ) . toLowerCase ( ) ;
94+ filtered = allUsers . filter ( user => {
95+ return (
96+ user . firstName ?. toLowerCase ( ) . includes ( searchTerm ) ||
97+ user . lastName ?. toLowerCase ( ) . includes ( searchTerm ) ||
98+ user . email ?. toLowerCase ( ) . includes ( searchTerm )
99+ ) ;
100+ } ) ;
101+ }
102+
103+ // Sort filtered results
104+ if ( currentSortOrder !== 'none' ) {
105+ filtered = [ ...filtered ] . sort ( ( a , b ) => {
106+ const aVal = a [ currentSortColumn ] ;
107+ const bVal = b [ currentSortColumn ] ;
108+
109+ // Handle null/undefined
110+ if ( aVal == null && bVal == null ) return 0 ;
111+ if ( aVal == null ) return 1 ;
112+ if ( bVal == null ) return - 1 ;
113+
114+ // Compare based on type
115+ let comparison = 0 ;
116+ if ( typeof aVal === 'string' ) {
117+ comparison = aVal . localeCompare ( bVal ) ;
118+ } else if ( typeof aVal === 'number' ) {
119+ comparison = aVal - bVal ;
120+ } else {
121+ // Handle dates
122+ const dateA = new Date ( aVal ) ;
123+ const dateB = new Date ( bVal ) ;
124+ comparison = dateA . getTime ( ) - dateB . getTime ( ) ;
125+ }
126+
127+ return currentSortOrder === 'asc' ? comparison : - comparison ;
128+ } ) ;
129+ }
130+
131+ setFilteredUsers ( filtered ) ;
132+ setTotal ( filtered . length ) ;
133+ setPage ( 0 ) ;
134+
135+ } , [ allUsers , query , currentSortColumn , currentSortOrder ] ) ;
111136
112137 function handleSortUsers ( columnName ) {
113138 if ( columnName === null ) {
@@ -179,37 +204,48 @@ export default function Overview() {
179204 // }
180205
181206 function maybeRenderPagination ( ) {
182- const amountOfUsersOnCurrentPage = Math . min ( ( page + 1 ) * rowsPerPage , users . length ) ;
183- const pageOffset = page * rowsPerPage ;
184- const endingElementNumber = amountOfUsersOnCurrentPage + pageOffset ;
185- if ( users . length ) {
186- return (
187- < nav className = 'flex justify-start py-6' >
188- < div className = 'flex items-center navbar-start' >
189- < span className = "text-gray-700 dark:text-white" >
190- { loading ? '...' : paginationText }
191- </ span >
192- </ div >
193- < div className = 'flex justify-end space-x-3 navbar-end' >
194- < button
195- className = 'btn btn-neutral text-gray-800 bg-gray-500 hover:bg-gray-300 dark:text-white dark:bg-gray-700 dark:hover:bg-gray-600'
196- onClick = { ( ) => setPage ( page - 1 ) }
197- disabled = { page === 0 || loading }
198- >
199- previous
200- </ button >
201- < button
202- className = 'btn btn-neutral text-gray-800 bg-gray-200 hover:bg-gray-300 dark:text-white dark:bg-gray-700 dark:hover:bg-gray-600'
203- onClick = { ( ) => setPage ( page + 1 ) }
204- disabled = { endingElementNumber >= total || loading }
205- >
206- next
207- </ button >
208- </ div >
209- </ nav >
210- ) ;
211- }
212- return < > </ > ;
207+ const ROWS_PER_PAGE = 20 ;
208+ const startIdx = page * ROWS_PER_PAGE ;
209+ const endIdx = Math . min ( startIdx + ROWS_PER_PAGE , total ) ;
210+
211+ if ( filteredUsers . length === 0 ) return < > </ > ;
212+
213+ return (
214+ < nav className = 'flex justify-start py-6' >
215+ < div className = 'flex items-center navbar-start' >
216+ < span className = "text-gray-700 dark:text-white" >
217+ { loading ? '...' : (
218+ < >
219+ < p className = 'md:hidden text-gray-700 dark:text-white' >
220+ { startIdx + 1 } - { endIdx } / { total }
221+ </ p >
222+ < p className = "hidden md:inline-block text-gray-700 dark:text-white" >
223+ Showing < span className = 'font-medium' > { startIdx + 1 } </ span > to{ ' ' }
224+ < span className = 'font-medium' > { endIdx } </ span > of{ ' ' }
225+ < span className = 'font-medium' > { total } </ span > results
226+ </ p >
227+ </ >
228+ ) }
229+ </ span >
230+ </ div >
231+ < div className = 'flex justify-end space-x-3 navbar-end' >
232+ < button
233+ className = 'btn btn-neutral text-gray-800 bg-gray-500 hover:bg-gray-300 dark:text-white dark:bg-gray-700 dark:hover:bg-gray-600'
234+ onClick = { ( ) => setPage ( page - 1 ) }
235+ disabled = { page === 0 || loading }
236+ >
237+ previous
238+ </ button >
239+ < button
240+ className = 'btn btn-neutral text-gray-800 bg-gray-200 hover:bg-gray-300 dark:text-white dark:bg-gray-700 dark:hover:bg-gray-600'
241+ onClick = { ( ) => setPage ( page + 1 ) }
242+ disabled = { endIdx >= total || loading }
243+ >
244+ next
245+ </ button >
246+ </ div >
247+ </ nav >
248+ ) ;
213249 }
214250
215251 return (
@@ -254,29 +290,14 @@ export default function Overview() {
254290 < div className = 'py-6' >
255291 < label className = "w-full form-control" >
256292 < div className = "label" >
257- < span className = "label-text text-md text-gray-700 dark:text-white" > Type a search, followed by the enter key </ span >
293+ < span className = "label-text text-md text-gray-700 dark:text-white" > Type a search</ span >
258294 </ div >
259295 < input
260296 className = "w-full text-sm input input-bordered text-gray-900 dark:text-white sm:text-base"
261297 type = "text"
262298 placeholder = "search by first name, last name, or email"
263- onKeyDown = { ( event ) => {
264- if ( event . key === 'Enter' ) {
265- // instead of calling the backend directory, set
266- // the page we are on to zero if the current page
267- // we are on isn't the first page (value of 0).
268- // by doing this, the useEffect will call the backend
269- // for us with the correct page and query.
270- if ( page ) {
271- setPage ( 0 ) ;
272- } else {
273- callDatabase ( ) ;
274- }
275- }
276- } }
277- onChange = { event => {
278- setQuery ( event . target . value ) ;
279- } }
299+ value = { query }
300+ onChange = { event => setQuery ( event . target . value ) }
280301 />
281302 </ label >
282303 </ div >
@@ -308,7 +329,7 @@ export default function Overview() {
308329 </ tr >
309330 </ thead >
310331 < tbody >
311- { users . map ( ( user ) => (
332+ { filteredUsers . slice ( page * 20 , ( page + 1 ) * 20 ) . map ( ( user ) => (
312333 < tr className = 'break-all !rounded md:break-keep hover:bg-gray-100 dark:hover:bg-white/10' key = { user . email } >
313334 < td className = '' >
314335 < a className = 'link link-hover text-blue-600 dark:text-blue-400' target = "_blank" rel = "noopener noreferrer" href = { `/user/edit/${ user . _id } ` } >
@@ -349,7 +370,7 @@ export default function Overview() {
349370
350371 </ tbody >
351372 </ table >
352- { users . length === 0 && (
373+ { filteredUsers . length === 0 && (
353374 < div className = 'flex flex-row w-100 justify-center' >
354375 < p className = 'text-lg text-gray-700 dark:text-white/70 mt-5 mb-5' > No results found!</ p >
355376 </ div >
0 commit comments