@@ -34,7 +34,11 @@ function formatTimestamp(iso: string): string {
3434 } ) ;
3535}
3636
37- async function fetchAuditEntries ( token : string , cursor ?: string , search ?: string ) : Promise < AuditResponse > {
37+ async function fetchAuditEntries (
38+ token : string ,
39+ cursor ?: string ,
40+ search ?: string
41+ ) : Promise < AuditResponse > {
3842 const params = new URLSearchParams ( ) ;
3943 if ( cursor ) params . set ( "cursor" , cursor ) ;
4044 if ( search ) params . set ( "search" , search ) ;
@@ -43,13 +47,13 @@ async function fetchAuditEntries(token: string, cursor?: string, search?: string
4347
4448function AuditPageInner ( ) {
4549 const { data : session } = useSession ( ) ;
46- const [ entries , setEntries ] = useState < AuditEntry [ ] > ( [ ] ) ;
47- const [ loading , setLoading ] = useState ( true ) ;
48- const [ search , setSearch ] = useState ( "" ) ;
49- const [ searchInput , setSearchInput ] = useState ( "" ) ;
50- const [ cursorStack , setCursorStack ] = useState < Array < string | null > > ( [ null ] ) ;
51- const [ nextCursor , setNextCursor ] = useState < string | null > ( null ) ;
52- const [ pageIndex , setPageIndex ] = useState ( 0 ) ;
50+ const [ entries , setEntries ] = useState < AuditEntry [ ] > ( [ ] ) ;
51+ const [ loading , setLoading ] = useState ( true ) ;
52+ const [ search , setSearch ] = useState ( "" ) ;
53+ const [ searchInput , setSearchInput ] = useState ( "" ) ;
54+ const [ cursorStack , setCursorStack ] = useState < Array < string | null > > ( [ null ] ) ;
55+ const [ nextCursor , setNextCursor ] = useState < string | null > ( null ) ;
56+ const [ pageIndex , setPageIndex ] = useState ( 0 ) ;
5357 const token = session ?. accessToken ?? "" ;
5458 const sessionError = session ?. error ;
5559
@@ -62,27 +66,34 @@ function AuditPageInner() {
6266 return ( ) => clearTimeout ( t ) ;
6367 } , [ searchInput ] ) ;
6468
65- const loadPage = useCallback ( async ( idx : number , cursor : string | null ) => {
66- if ( ! token ) return ;
67- setLoading ( true ) ;
68- try {
69- const data = await fetchAuditEntries ( token , cursor ?? undefined , search ) ;
70- setEntries ( data . entries ) ;
71- setNextCursor ( data . cursor ) ;
72- setPageIndex ( idx ) ;
73- setCursorStack ( ( prev ) => {
74- if ( idx + 1 < prev . length ) return prev ;
75- if ( ! data . cursor ) return prev ;
76- const next = [ ...prev ] ;
77- next [ idx + 1 ] = data . cursor ;
78- return next ;
79- } ) ;
80- } catch ( err ) {
81- console . error ( "Failed to load audit entries" ) ;
82- } finally {
83- setLoading ( false ) ;
84- }
85- } , [ token , search ] ) ;
69+ const loadPage = useCallback (
70+ async ( idx : number , cursor : string | null ) => {
71+ if ( ! token ) return ;
72+ setLoading ( true ) ;
73+ try {
74+ const data = await fetchAuditEntries (
75+ token ,
76+ cursor ?? undefined ,
77+ search
78+ ) ;
79+ setEntries ( data . entries ) ;
80+ setNextCursor ( data . cursor ) ;
81+ setPageIndex ( idx ) ;
82+ setCursorStack ( ( prev ) => {
83+ if ( idx + 1 < prev . length ) return prev ;
84+ if ( ! data . cursor ) return prev ;
85+ const next = [ ...prev ] ;
86+ next [ idx + 1 ] = data . cursor ;
87+ return next ;
88+ } ) ;
89+ } catch ( err ) {
90+ console . error ( "Failed to load audit entries" ) ;
91+ } finally {
92+ setLoading ( false ) ;
93+ }
94+ } ,
95+ [ token , search ]
96+ ) ;
8697
8798 useEffect ( ( ) => {
8899 setCursorStack ( [ null ] ) ;
@@ -94,7 +105,8 @@ function AuditPageInner() {
94105 const hasPrev = pageIndex > 0 ;
95106 const hasNext = nextCursor !== null ;
96107
97- const goPrev = ( ) => hasPrev && loadPage ( pageIndex - 1 , cursorStack [ pageIndex - 1 ] ) ;
108+ const goPrev = ( ) =>
109+ hasPrev && loadPage ( pageIndex - 1 , cursorStack [ pageIndex - 1 ] ) ;
98110 const goNext = ( ) => {
99111 if ( ! hasNext ) return ;
100112 const nextIdx = pageIndex + 1 ;
@@ -104,10 +116,28 @@ function AuditPageInner() {
104116 const PaginationControls = ( ) => (
105117 < ul className = "pagination pagination-sm justify-content-center mb-0" >
106118 < li className = { `page-item ${ ! hasPrev ? "disabled" : "" } ` } >
107- < a className = "page-link" href = "#" onClick = { ( e ) => { e . preventDefault ( ) ; goPrev ( ) ; } } > Prev</ a >
119+ < a
120+ className = "page-link"
121+ href = "#"
122+ onClick = { ( e ) => {
123+ e . preventDefault ( ) ;
124+ goPrev ( ) ;
125+ } }
126+ >
127+ Prev
128+ </ a >
108129 </ li >
109130 < li className = { `page-item ${ ! hasNext ? "disabled" : "" } ` } >
110- < a className = "page-link" href = "#" onClick = { ( e ) => { e . preventDefault ( ) ; goNext ( ) ; } } > Next</ a >
131+ < a
132+ className = "page-link"
133+ href = "#"
134+ onClick = { ( e ) => {
135+ e . preventDefault ( ) ;
136+ goNext ( ) ;
137+ } }
138+ >
139+ Next
140+ </ a >
111141 </ li >
112142 </ ul >
113143 ) ;
@@ -117,7 +147,9 @@ function AuditPageInner() {
117147 < div className = "row mb-3 align-items-center" >
118148 < div className = "col-12 col-md-4 mb-2 mb-md-0" >
119149 < div className = "input-group" >
120- < span className = "input-group-text" > < Icon path = { mdiMagnify } size = { 0.75 } /> </ span >
150+ < span className = "input-group-text" >
151+ < Icon path = { mdiMagnify } size = { 0.75 } />
152+ </ span >
121153 < input
122154 type = "text"
123155 className = "form-control"
@@ -131,7 +163,10 @@ function AuditPageInner() {
131163
132164 < div className = "card" >
133165 < div className = "card-header d-flex justify-content-between align-items-center" >
134- < span > < Icon path = { mdiHistory } size = { 0.85 } className = "me-2" /> Audit Logs</ span >
166+ < span >
167+ < Icon path = { mdiHistory } size = { 0.85 } className = "me-2" />
168+ Audit Logs
169+ </ span >
135170 </ div >
136171 < div className = "card-body py-2 border-bottom" >
137172 < PaginationControls />
@@ -143,17 +178,32 @@ function AuditPageInner() {
143178 </ div >
144179 ) : entries . length === 0 ? (
145180 < div className = "card-body text-center py-5 text-muted" >
146- < Icon path = { mdiHistory } size = { 2 } className = "mb-3 opacity-25 d-block mx-auto" />
181+ < Icon
182+ path = { mdiHistory }
183+ size = { 2 }
184+ className = "mb-3 opacity-25 d-block mx-auto"
185+ />
147186 < p className = "mb-0" > No entries match your filters.</ p >
148187 { search && (
149- < button className = "btn btn-link btn-sm mt-2" onClick = { ( ) => { setSearchInput ( "" ) ; setSearch ( "" ) ; } } >
188+ < button
189+ className = "btn btn-link btn-sm mt-2"
190+ onClick = { ( ) => {
191+ setSearchInput ( "" ) ;
192+ setSearch ( "" ) ;
193+ } }
194+ >
150195 Clear filters
151196 </ button >
152197 ) }
153198 </ div >
154199 ) : (
155200 < div className = "table-responsive" >
156- < Table hover size = "sm" className = "mb-0" style = { { fontSize : "0.875rem" } } >
201+ < Table
202+ hover
203+ size = "sm"
204+ className = "mb-0"
205+ style = { { fontSize : "0.875rem" } }
206+ >
157207 < thead >
158208 < tr >
159209 < th style = { { width : "16%" } } > Timestamp</ th >
@@ -166,10 +216,14 @@ function AuditPageInner() {
166216 < tbody >
167217 { entries . map ( ( entry ) => (
168218 < tr key = { entry . _id } >
169- < td style = { { whiteSpace : "nowrap" } } > { formatTimestamp ( entry . timestamp ) } </ td >
219+ < td style = { { whiteSpace : "nowrap" } } >
220+ { formatTimestamp ( entry . timestamp ) }
221+ </ td >
170222 < td > { entry . username } </ td >
171223 < td > { entry . name } </ td >
172- < td > < span > { entry . action } </ span > </ td >
224+ < td >
225+ < span > { entry . action } </ span >
226+ </ td >
173227 < td > { entry . reason } </ td >
174228 </ tr >
175229 ) ) }
@@ -182,9 +236,7 @@ function AuditPageInner() {
182236 className = "card-footer d-grid align-items-center"
183237 style = { { gridTemplateColumns : "1fr auto 1fr" } }
184238 >
185- < small className = "text-muted" >
186- Page { pageIndex + 1 }
187- </ small >
239+ < small className = "text-muted" > Page { pageIndex + 1 } </ small >
188240 < div className = "justify-self-center" >
189241 < PaginationControls />
190242 </ div >
@@ -201,4 +253,4 @@ export default function AuditPage() {
201253 < AuditPageInner />
202254 </ AuthGate >
203255 ) ;
204- }
256+ }
0 commit comments