66 TableContainer , TableHead , TableRow , TextField , Button ,
77 Select , MenuItem , FormControl , InputLabel , Alert , Chip ,
88 LinearProgress , TablePagination , useMediaQuery , useTheme ,
9+ InputAdornment ,
910} from "@mui/material" ;
11+ import { parseUnits } from "viem" ;
1012import { CONTRACTS } from "@/config/contracts" ;
1113import { useRewardTypes } from "@/hooks/useRewardsProgram" ;
1214import { useChunkedEventLogs , type TimeRange } from "@/hooks/useChunkedEventLogs" ;
@@ -18,12 +20,18 @@ export default function ReportsPage() {
1820 const { data : rewardTypesData } = useRewardTypes ( ) ;
1921
2022 const [ filterProgramId , setFilterProgramId ] = useState ( "" ) ;
21- const [ filterRewardType , setFilterRewardType ] = useState < number | "" > ( "" ) ;
2223 const [ timeRange , setTimeRange ] = useState < TimeRange > ( "7d" ) ;
2324 const [ trigger , setTrigger ] = useState ( 0 ) ;
2425 const [ page , setPage ] = useState ( 0 ) ;
2526 const [ rowsPerPage , setRowsPerPage ] = useState ( 25 ) ;
2627
28+ // Client-side filters
29+ const [ filterEventType , setFilterEventType ] = useState < string > ( "" ) ;
30+ const [ filterRewardType , setFilterRewardType ] = useState < number | "" > ( "" ) ;
31+ const [ filterWallet , setFilterWallet ] = useState ( "" ) ;
32+ const [ filterAmountMin , setFilterAmountMin ] = useState ( "" ) ;
33+ const [ filterAmountMax , setFilterAmountMax ] = useState ( "" ) ;
34+
2735 const {
2836 events, loading, progress, totalChunks, completedChunks, error, cancel,
2937 } = useChunkedEventLogs ( {
@@ -33,10 +41,30 @@ export default function ReportsPage() {
3341 trigger,
3442 } ) ;
3543
36- // Client-side reward type filter
37- const filteredEvents = filterRewardType !== ""
38- ? events . filter ( r => r . type !== "Deposit" || r . rewardType === filterRewardType )
39- : events ;
44+ // Client-side filter chain
45+ let filteredEvents = events ;
46+ if ( filterEventType ) {
47+ filteredEvents = filteredEvents . filter ( e => e . type === filterEventType ) ;
48+ }
49+ if ( filterRewardType !== "" ) {
50+ filteredEvents = filteredEvents . filter ( r => r . type !== "Deposit" || r . rewardType === filterRewardType ) ;
51+ }
52+ if ( filterWallet ) {
53+ const w = filterWallet . toLowerCase ( ) ;
54+ filteredEvents = filteredEvents . filter ( e => e . wallet . toLowerCase ( ) . includes ( w ) ) ;
55+ }
56+ if ( filterAmountMin ) {
57+ try {
58+ const min = parseUnits ( filterAmountMin , 18 ) ;
59+ filteredEvents = filteredEvents . filter ( e => e . amount >= min ) ;
60+ } catch { /* invalid input, skip */ }
61+ }
62+ if ( filterAmountMax ) {
63+ try {
64+ const max = parseUnits ( filterAmountMax , 18 ) ;
65+ filteredEvents = filteredEvents . filter ( e => e . amount <= max ) ;
66+ } catch { /* invalid input, skip */ }
67+ }
4068
4169 // Summary stats
4270 const totalDeposits = filteredEvents . filter ( e => e . type === "Deposit" ) . reduce ( ( s , e ) => s + e . amount , BigInt ( 0 ) ) ;
@@ -60,25 +88,26 @@ export default function ReportsPage() {
6088 }
6189 } ;
6290
63- // Pagination
91+ // Pagination — clamp page if filters reduce result count
92+ const safePage = page * rowsPerPage >= filteredEvents . length && filteredEvents . length > 0 ? 0 : page ;
6493 const paginatedEvents = filteredEvents . slice (
65- page * rowsPerPage ,
66- page * rowsPerPage + rowsPerPage
94+ safePage * rowsPerPage ,
95+ safePage * rowsPerPage + rowsPerPage
6796 ) ;
6897
6998 return (
7099 < Box >
71100 < Typography variant = "h4" gutterBottom > Reports</ Typography >
72101
73102 < Paper sx = { { p : 3 , mb : 3 } } >
74- < Typography variant = "h6" gutterBottom > Filters </ Typography >
103+ < Typography variant = "h6" gutterBottom > Fetch Settings </ Typography >
75104 < Grid container spacing = { 2 } alignItems = "center" >
76- < Grid item xs = { 12 } sm = { 3 } >
105+ < Grid item xs = { 12 } sm = { 4 } >
77106 < TextField label = "Program ID" value = { filterProgramId }
78107 onChange = { ( e ) => setFilterProgramId ( e . target . value ) }
79108 type = "number" fullWidth size = "small" placeholder = "All programs" />
80109 </ Grid >
81- < Grid item xs = { 12 } sm = { 3 } >
110+ < Grid item xs = { 12 } sm = { 4 } >
82111 < FormControl fullWidth size = "small" >
83112 < InputLabel > Time Range</ InputLabel >
84113 < Select value = { timeRange } onChange = { ( e ) => setTimeRange ( e . target . value as TimeRange ) } label = "Time Range" >
@@ -89,18 +118,7 @@ export default function ReportsPage() {
89118 </ Select >
90119 </ FormControl >
91120 </ Grid >
92- < Grid item xs = { 12 } sm = { 3 } >
93- < FormControl fullWidth size = "small" >
94- < InputLabel > Reward Type</ InputLabel >
95- < Select value = { filterRewardType } onChange = { ( e ) => setFilterRewardType ( e . target . value as number | "" ) } label = "Reward Type" >
96- < MenuItem value = "" > All</ MenuItem >
97- { Object . entries ( rewardTypeNames ) . map ( ( [ k , v ] ) => (
98- < MenuItem key = { k } value = { Number ( k ) } > { v } </ MenuItem >
99- ) ) }
100- </ Select >
101- </ FormControl >
102- </ Grid >
103- < Grid item xs = { 12 } sm = { 3 } >
121+ < Grid item xs = { 12 } sm = { 4 } >
104122 < Button
105123 variant = "contained"
106124 onClick = { handleGenerate }
@@ -113,6 +131,54 @@ export default function ReportsPage() {
113131 </ Grid >
114132 </ Paper >
115133
134+ { events . length > 0 && (
135+ < Paper sx = { { p : 3 , mb : 3 } } >
136+ < Typography variant = "h6" gutterBottom > Filter Results</ Typography >
137+ < Grid container spacing = { 2 } alignItems = "center" >
138+ < Grid item xs = { 12 } sm = { 2 } >
139+ < FormControl fullWidth size = "small" >
140+ < InputLabel > Event Type</ InputLabel >
141+ < Select value = { filterEventType } onChange = { ( e ) => { setFilterEventType ( e . target . value ) ; setPage ( 0 ) ; } } label = "Event Type" >
142+ < MenuItem value = "" > All</ MenuItem >
143+ < MenuItem value = "Deposit" > Deposit</ MenuItem >
144+ < MenuItem value = "Transfer" > Transfer</ MenuItem >
145+ < MenuItem value = "TransferToParent" > To Parent</ MenuItem >
146+ < MenuItem value = "Withdrawal" > Withdrawal</ MenuItem >
147+ </ Select >
148+ </ FormControl >
149+ </ Grid >
150+ < Grid item xs = { 12 } sm = { 2 } >
151+ < FormControl fullWidth size = "small" >
152+ < InputLabel > Reward Type</ InputLabel >
153+ < Select value = { filterRewardType } onChange = { ( e ) => { setFilterRewardType ( e . target . value as number | "" ) ; setPage ( 0 ) ; } } label = "Reward Type" >
154+ < MenuItem value = "" > All</ MenuItem >
155+ { Object . entries ( rewardTypeNames ) . map ( ( [ k , v ] ) => (
156+ < MenuItem key = { k } value = { Number ( k ) } > { v } </ MenuItem >
157+ ) ) }
158+ </ Select >
159+ </ FormControl >
160+ </ Grid >
161+ < Grid item xs = { 12 } sm = { 4 } >
162+ < TextField label = "Wallet Address" value = { filterWallet }
163+ onChange = { ( e ) => { setFilterWallet ( e . target . value ) ; setPage ( 0 ) ; } }
164+ fullWidth size = "small" placeholder = "0x..." />
165+ </ Grid >
166+ < Grid item xs = { 6 } sm = { 2 } >
167+ < TextField label = "Min Amount" value = { filterAmountMin }
168+ onChange = { ( e ) => { setFilterAmountMin ( e . target . value ) ; setPage ( 0 ) ; } }
169+ type = "number" fullWidth size = "small"
170+ InputProps = { { endAdornment : < InputAdornment position = "end" > FULA</ InputAdornment > } } />
171+ </ Grid >
172+ < Grid item xs = { 6 } sm = { 2 } >
173+ < TextField label = "Max Amount" value = { filterAmountMax }
174+ onChange = { ( e ) => { setFilterAmountMax ( e . target . value ) ; setPage ( 0 ) ; } }
175+ type = "number" fullWidth size = "small"
176+ InputProps = { { endAdornment : < InputAdornment position = "end" > FULA</ InputAdornment > } } />
177+ </ Grid >
178+ </ Grid >
179+ </ Paper >
180+ ) }
181+
116182 { loading && (
117183 < Paper sx = { { p : 2 , mb : 3 } } >
118184 < Box sx = { { display : "flex" , justifyContent : "space-between" , mb : 1 } } >
@@ -203,7 +269,7 @@ export default function ReportsPage() {
203269 < TablePagination
204270 component = "div"
205271 count = { filteredEvents . length }
206- page = { page }
272+ page = { safePage }
207273 onPageChange = { ( _ , p ) => setPage ( p ) }
208274 rowsPerPage = { rowsPerPage }
209275 onRowsPerPageChange = { ( e ) => {
0 commit comments