@@ -27,7 +27,7 @@ import { QRScannerButton } from "@/components/common/QRScannerButton";
2727
2828/* -- Per-program membership row -- */
2929
30- function MemberProgramRow ( { memberID, programId } : { memberID : string ; programId : number } ) {
30+ function MemberProgramCard ( { memberID, programId, isMobile } : { memberID : string ; programId : number ; isMobile : boolean } ) {
3131 const memberIDBytes = toBytes12 ( memberID ) ;
3232
3333 const { data : member } = useReadContract ( {
@@ -57,10 +57,55 @@ function MemberProgramRow({ memberID, programId }: { memberID: string; programId
5757
5858 if ( ! member || ! member . active ) return null ;
5959
60+ const programName = program ? program . name : `Program #${ programId } ` ;
61+ const programCode = program ? fromBytes8 ( program . code as `0x${string } `) : "" ;
62+
63+ if ( isMobile ) {
64+ return (
65+ < Paper sx = { { p : 2 , mb : 1.5 } } variant = "outlined" >
66+ < Box sx = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , mb : 1 } } >
67+ < Box >
68+ < Typography variant = "subtitle2" > { programName } </ Typography >
69+ { programCode && < Typography variant = "caption" color = "text.secondary" > { programCode } (P{ programId } )</ Typography > }
70+ </ Box >
71+ < Box sx = { { display : "flex" , gap : 0.5 } } >
72+ < Chip label = { MemberRoleLabels [ Number ( member . role ) ] || "Unknown" } size = "small"
73+ color = { Number ( member . role ) === 3 ? "primary" : Number ( member . role ) === 2 ? "secondary" : "default" } />
74+ < Chip label = { MemberTypeLabels [ Number ( member . memberType ) ] || "Free" } size = "small" variant = "outlined" />
75+ </ Box >
76+ </ Box >
77+ < Box sx = { { display : "flex" , gap : 2 , mb : 1 } } >
78+ < Box sx = { { flex : 1 , textAlign : "center" } } >
79+ < Typography variant = "caption" color = "text.secondary" display = "block" > Available</ Typography >
80+ < Typography variant = "body2" sx = { { color : "success.main" , fontWeight : 600 } } >
81+ { balance ? formatFula ( balance [ 0 ] ) : "-" }
82+ </ Typography >
83+ </ Box >
84+ < Box sx = { { flex : 1 , textAlign : "center" } } >
85+ < Typography variant = "caption" color = "text.secondary" display = "block" > Locked</ Typography >
86+ < Typography variant = "body2" sx = { { color : "error.main" , fontWeight : 600 } } >
87+ { balance ? formatFula ( balance [ 1 ] ) : "-" }
88+ </ Typography >
89+ </ Box >
90+ < Box sx = { { flex : 1 , textAlign : "center" } } >
91+ < Typography variant = "caption" color = "text.secondary" display = "block" > Time-Locked</ Typography >
92+ < Typography variant = "body2" sx = { { color : "warning.main" , fontWeight : 600 } } >
93+ { balance ? formatFula ( balance [ 2 ] ) : "-" }
94+ </ Typography >
95+ </ Box >
96+ </ Box >
97+ < Box sx = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" } } >
98+ < Typography variant = "caption" color = "text.secondary" > Parent: { shortenAddress ( member . parent ) } </ Typography >
99+ < QRCodeDisplay programId = { programId } memberID = { memberID } size = { 80 } />
100+ </ Box >
101+ </ Paper >
102+ ) ;
103+ }
104+
60105 return (
61106 < TableRow >
62- < TableCell > { program ? program . name : `Program # ${ programId } ` } </ TableCell >
63- < TableCell sx = { { display : { xs : "none" , sm : "table-cell" } } } > { program ? fromBytes8 ( program . code as `0x${ string } ` ) : "-" } </ TableCell >
107+ < TableCell > { programName } </ TableCell >
108+ < TableCell > { programCode || "-" } </ TableCell >
64109 < TableCell >
65110 < Chip label = { MemberRoleLabels [ Number ( member . role ) ] || "Unknown" } size = "small"
66111 color = { Number ( member . role ) === 3 ? "primary" : Number ( member . role ) === 2 ? "secondary" : "default" } />
@@ -70,8 +115,8 @@ function MemberProgramRow({ memberID, programId }: { memberID: string; programId
70115 </ TableCell >
71116 < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > { shortenAddress ( member . parent ) } </ TableCell >
72117 < TableCell sx = { { color : "success.main" } } > { balance ? formatFula ( balance [ 0 ] ) : "-" } </ TableCell >
73- < TableCell sx = { { color : "error.main" , display : { xs : "none" , sm : "table-cell" } } } > { balance ? formatFula ( balance [ 1 ] ) : "-" } </ TableCell >
74- < TableCell sx = { { color : "warning.main" , display : { xs : "none" , sm : "table-cell" } } } > { balance ? formatFula ( balance [ 2 ] ) : "-" } </ TableCell >
118+ < TableCell sx = { { color : "error.main" } } > { balance ? formatFula ( balance [ 1 ] ) : "-" } </ TableCell >
119+ < TableCell sx = { { color : "warning.main" } } > { balance ? formatFula ( balance [ 2 ] ) : "-" } </ TableCell >
75120 < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > { member . active ? "Active" : "Inactive" } </ TableCell >
76121 < TableCell >
77122 < QRCodeDisplay programId = { programId } memberID = { memberID } size = { 120 } />
@@ -397,33 +442,48 @@ function BalanceContent() {
397442 { memberExists && (
398443 < >
399444 < Paper sx = { { p : 2 , mt : 2 } } >
400- < Typography variant = "h6" gutterBottom > Programs & Balances for & quot ; { searchID } "</ Typography >
445+ < Typography variant = "h6" gutterBottom sx = { { fontSize : { xs : "1rem" , sm : "1.25rem" } } } >
446+ Programs & Balances for & quot ; { searchID } "
447+ </ Typography >
401448 < Typography variant = "caption" color = "text.secondary" sx = { { mb : 2 , display : "block" } } >
402- Balance columns : Available / Permanently Locked / Time-Locked (FULA)
449+ Balance: Available / Permanently Locked / Time-Locked (FULA)
403450 </ Typography >
404- < TableContainer >
405- < Table size = "small" >
406- < TableHead >
407- < TableRow >
408- < TableCell > Program</ TableCell >
409- < TableCell sx = { { display : { xs : "none" , sm : "table-cell" } } } > Code</ TableCell >
410- < TableCell > Role</ TableCell >
411- < TableCell > Type</ TableCell >
412- < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > Parent</ TableCell >
413- < TableCell > Available</ TableCell >
414- < TableCell sx = { { display : { xs : "none" , sm : "table-cell" } } } > Locked</ TableCell >
415- < TableCell sx = { { display : { xs : "none" , sm : "table-cell" } } } > Time-Locked</ TableCell >
416- < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > Status</ TableCell >
417- < TableCell > QR</ TableCell >
418- </ TableRow >
419- </ TableHead >
420- < TableBody >
421- { Array . from ( { length : count } , ( _ , i ) => (
422- < MemberProgramRow key = { i + 1 } memberID = { searchID } programId = { i + 1 } />
423- ) ) }
424- </ TableBody >
425- </ Table >
426- </ TableContainer >
451+
452+ { /* Mobile: card layout */ }
453+ { isMobile && (
454+ < Box >
455+ { Array . from ( { length : count } , ( _ , i ) => (
456+ < MemberProgramCard key = { i + 1 } memberID = { searchID } programId = { i + 1 } isMobile />
457+ ) ) }
458+ </ Box >
459+ ) }
460+
461+ { /* Desktop: table layout */ }
462+ { ! isMobile && (
463+ < TableContainer >
464+ < Table size = "small" >
465+ < TableHead >
466+ < TableRow >
467+ < TableCell > Program</ TableCell >
468+ < TableCell > Code</ TableCell >
469+ < TableCell > Role</ TableCell >
470+ < TableCell > Type</ TableCell >
471+ < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > Parent</ TableCell >
472+ < TableCell > Available</ TableCell >
473+ < TableCell > Locked</ TableCell >
474+ < TableCell > Time-Locked</ TableCell >
475+ < TableCell sx = { { display : { xs : "none" , md : "table-cell" } } } > Status</ TableCell >
476+ < TableCell > QR</ TableCell >
477+ </ TableRow >
478+ </ TableHead >
479+ < TableBody >
480+ { Array . from ( { length : count } , ( _ , i ) => (
481+ < MemberProgramCard key = { i + 1 } memberID = { searchID } programId = { i + 1 } isMobile = { false } />
482+ ) ) }
483+ </ TableBody >
484+ </ Table >
485+ </ TableContainer >
486+ ) }
427487 </ Paper >
428488
429489 { memberWallet && < OwnerActions memberWallet = { memberWallet } initialProgramId = { claimedProgramId ?? undefined } /> }
0 commit comments