@@ -10,9 +10,11 @@ import { useAccount, useReadContract, useReadContracts } from "wagmi";
1010import { useSearchParams } from "next/navigation" ;
1111import { zeroAddress } from "viem" ;
1212import { CONTRACTS , REWARDS_PROGRAM_ABI , MemberRoleLabels } from "@/config/contracts" ;
13- import { toBytes12 , fromBytes12 , fromBytes8 , shortenAddress , formatFula , formatDate , formatContractError } from "@/lib/utils" ;
14- import { useProgramCount , useProgram , useTimeLocks , useDirectChildren , useTransferToParent , useWithdraw , useApproveToken , useAddTokens } from "@/hooks/useRewardsProgram" ;
13+ import { toBytes12 , fromBytes12 , fromBytes8 , shortenAddress , formatFula , formatContractError } from "@/lib/utils" ;
14+ import { useProgramCount , useProgram , useTransferToParent , useWithdraw , useApproveToken , useAddTokens } from "@/hooks/useRewardsProgram" ;
1515import { OnChainDisclaimer } from "@/components/common/OnChainDisclaimer" ;
16+ import { QRCodeDisplay } from "@/components/common/QRCodeDisplay" ;
17+ import { QRScannerButton } from "@/components/common/QRScannerButton" ;
1618
1719/* -- Per-program membership row -- */
1820
@@ -32,12 +34,12 @@ function MemberProgramRow({ memberID, programId }: { memberID: string; programId
3234 const { data : balance } = useReadContract ( {
3335 address : CONTRACTS . rewardsProgram ,
3436 abi : REWARDS_PROGRAM_ABI ,
35- functionName : "getEffectiveBalance " ,
37+ functionName : "getBalance " ,
3638 args : member ?. wallet && member . wallet !== zeroAddress ? [ programId , member . wallet as `0x${string } `] : undefined ,
3739 query : { enabled : ! ! member ?. wallet && member . wallet !== zeroAddress } ,
3840 } ) ;
3941
40- if ( ! member || ! member . wallet || member . wallet === zeroAddress ) return null ;
42+ if ( ! member || ! member . active ) return null ;
4143
4244 return (
4345 < TableRow >
@@ -52,6 +54,9 @@ function MemberProgramRow({ memberID, programId }: { memberID: string; programId
5254 < TableCell sx = { { color : "error.main" } } > { balance ? formatFula ( balance [ 1 ] ) : "-" } </ TableCell >
5355 < TableCell sx = { { color : "warning.main" } } > { balance ? formatFula ( balance [ 2 ] ) : "-" } </ TableCell >
5456 < TableCell > { member . active ? "Active" : "Inactive" } </ TableCell >
57+ < TableCell >
58+ < QRCodeDisplay programId = { programId } memberID = { memberID } size = { 64 } />
59+ </ TableCell >
5560 </ TableRow >
5661 ) ;
5762}
@@ -146,95 +151,6 @@ function OwnerActions({ memberWallet }: { memberWallet: string }) {
146151 ) ;
147152}
148153
149- /* -- Time Locks detail -- */
150-
151- function TimeLockDetails ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
152- const { data : timeLocks } = useTimeLocks ( programId , wallet ) ;
153-
154- if ( ! timeLocks || timeLocks . length === 0 ) return null ;
155-
156- return (
157- < Box sx = { { mt : 1 } } >
158- { timeLocks . map ( ( lock , i ) => (
159- < Typography key = { i } variant = "body2" color = "text.secondary" >
160- { formatFula ( BigInt ( lock . amount ) ) } FULA - unlocks { formatDate ( Number ( lock . unlockTime ) ) }
161- </ Typography >
162- ) ) }
163- </ Box >
164- ) ;
165- }
166-
167- /* -- Sub-member row -- */
168-
169- function SubMemberRow ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
170- const { data : member } = useReadContract ( {
171- address : CONTRACTS . rewardsProgram ,
172- abi : REWARDS_PROGRAM_ABI ,
173- functionName : "getMember" ,
174- args : [ programId , wallet ] ,
175- } ) ;
176- const { data : balance } = useReadContract ( {
177- address : CONTRACTS . rewardsProgram ,
178- abi : REWARDS_PROGRAM_ABI ,
179- functionName : "getEffectiveBalance" ,
180- args : [ programId , wallet ] ,
181- } ) ;
182-
183- if ( ! member ) return null ;
184-
185- return (
186- < TableRow >
187- < TableCell > { fromBytes12 ( member . memberID as `0x${string } `) } </ TableCell >
188- < TableCell sx = { { fontFamily : "monospace" , fontSize : "0.85rem" } } > { shortenAddress ( member . wallet ) } </ TableCell >
189- < TableCell >
190- < Chip label = { MemberRoleLabels [ Number ( member . role ) ] || "Unknown" } size = "small"
191- color = { Number ( member . role ) === 3 ? "primary" : Number ( member . role ) === 2 ? "secondary" : "default" } />
192- </ TableCell >
193- < TableCell sx = { { color : "success.main" } } > { balance ? formatFula ( balance [ 0 ] ) : "-" } </ TableCell >
194- < TableCell sx = { { color : "error.main" } } > { balance ? formatFula ( balance [ 1 ] ) : "-" } </ TableCell >
195- < TableCell sx = { { color : "warning.main" } } > { balance ? formatFula ( balance [ 2 ] ) : "-" } </ TableCell >
196- < TableCell > { member . active ? "Active" : "Inactive" } </ TableCell >
197- </ TableRow >
198- ) ;
199- }
200-
201- /* -- Sub-members for a program -- */
202-
203- function SubMembersSection ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
204- const { data : children } = useDirectChildren ( programId , wallet ) ;
205- const { data : program } = useProgram ( programId ) ;
206-
207- if ( ! children || children . length === 0 ) return null ;
208-
209- return (
210- < Box sx = { { mt : 2 } } >
211- < Typography variant = "subtitle2" gutterBottom >
212- Sub-members in { program ? program . name : `Program #${ programId } ` }
213- </ Typography >
214- < TableContainer >
215- < Table size = "small" >
216- < TableHead >
217- < TableRow >
218- < TableCell > Member ID</ TableCell >
219- < TableCell > Wallet</ TableCell >
220- < TableCell > Role</ TableCell >
221- < TableCell > Withdrawable</ TableCell >
222- < TableCell > Locked</ TableCell >
223- < TableCell > Time-Locked</ TableCell >
224- < TableCell > Status</ TableCell >
225- </ TableRow >
226- </ TableHead >
227- < TableBody >
228- { children . map ( ( child ) => (
229- < SubMemberRow key = { child } programId = { programId } wallet = { child as `0x${string } `} />
230- ) ) }
231- </ TableBody >
232- </ Table >
233- </ TableContainer >
234- </ Box >
235- ) ;
236- }
237-
238154/* -- Main balance view -- */
239155
240156function BalanceContent ( ) {
@@ -274,10 +190,29 @@ function BalanceContent() {
274190 }
275191 }
276192
193+ // Check if member exists in any program (even walletless)
194+ let memberExists = false ;
195+ if ( multicallResults ) {
196+ for ( const result of multicallResults ) {
197+ if ( result . status === "success" && result . result ) {
198+ const member = result . result as { active : boolean } ;
199+ if ( member . active ) {
200+ memberExists = true ;
201+ break ;
202+ }
203+ }
204+ }
205+ }
206+
277207 const handleSearch = ( ) => {
278208 setSearchID ( memberID ) ;
279209 } ;
280210
211+ const handleQRScan = ( { programId : _p , memberID : m } : { programId : number ; memberID : string } ) => {
212+ setMemberID ( m ) ;
213+ setSearchID ( m ) ;
214+ } ;
215+
281216 return (
282217 < Box >
283218 < Typography variant = "h4" gutterBottom > Member Balance</ Typography >
@@ -292,6 +227,10 @@ function BalanceContent() {
292227 inputProps = { { maxLength : 12 } }
293228 placeholder = "Enter member ID..."
294229 />
230+ < QRScannerButton
231+ tooltip = "Scan member QR to search"
232+ onScan = { handleQRScan }
233+ />
295234 < Button variant = "contained" onClick = { handleSearch } disabled = { ! memberID } >
296235 Look Up
297236 </ Button >
@@ -308,16 +247,20 @@ function BalanceContent() {
308247 </ Paper >
309248 ) }
310249
311- { ! memberWallet && (
250+ { ! memberExists && (
312251 < Alert severity = "warning" sx = { { mb : 3 } } > No member found with ID "{ searchID } " in any program.</ Alert >
313252 ) }
314253
315- { memberWallet && (
254+ { memberExists && ! memberWallet && (
255+ < Alert severity = "info" sx = { { mb : 3 } } > Member "{ searchID } " exists but has no linked wallet (walletless member).</ Alert >
256+ ) }
257+
258+ { memberExists && (
316259 < >
317260 < Paper sx = { { p : 2 } } >
318261 < Typography variant = "h6" gutterBottom > Programs & Balances for & quot ; { searchID } "</ Typography >
319262 < Typography variant = "caption" color = "text.secondary" sx = { { mb : 2 , display : "block" } } >
320- Balance columns: Withdrawable / Permanently Locked / Time-Locked (FULA)
263+ Balance columns: Available / Permanently Locked / Time-Locked (FULA)
321264 </ Typography >
322265 < TableContainer >
323266 < Table size = "small" >
@@ -327,10 +270,11 @@ function BalanceContent() {
327270 < TableCell > Code</ TableCell >
328271 < TableCell > Role</ TableCell >
329272 < TableCell > Parent</ TableCell >
330- < TableCell > Withdrawable </ TableCell >
273+ < TableCell > Available </ TableCell >
331274 < TableCell > Locked</ TableCell >
332275 < TableCell > Time-Locked</ TableCell >
333276 < TableCell > Status</ TableCell >
277+ < TableCell > QR</ TableCell >
334278 </ TableRow >
335279 </ TableHead >
336280 < TableBody >
@@ -342,15 +286,7 @@ function BalanceContent() {
342286 </ TableContainer >
343287 </ Paper >
344288
345- { /* Sub-members per program */ }
346- < Paper sx = { { p : 2 , mt : 3 } } >
347- < Typography variant = "h6" gutterBottom > Sub-Members</ Typography >
348- { Array . from ( { length : count } , ( _ , i ) => (
349- < SubMembersSection key = { i + 1 } programId = { i + 1 } wallet = { memberWallet as `0x${string } `} />
350- ) ) }
351- </ Paper >
352-
353- < OwnerActions memberWallet = { memberWallet } />
289+ { memberWallet && < OwnerActions memberWallet = { memberWallet } /> }
354290 </ >
355291 ) }
356292 </ >
0 commit comments