77 DialogContent , DialogActions , Alert , CircularProgress ,
88 Select , MenuItem , FormControl , InputLabel , IconButton , Tooltip ,
99 Accordion , AccordionSummary , AccordionDetails , useMediaQuery , useTheme ,
10+ Tabs , Tab ,
1011} from "@mui/material" ;
1112import ArrowBackIcon from "@mui/icons-material/ArrowBack" ;
1213import EditIcon from "@mui/icons-material/Edit" ;
@@ -304,6 +305,8 @@ function ProgramDetail({ programId }: { programId: number }) {
304305 const [ parentDisclaimer , setParentDisclaimer ] = useState ( false ) ;
305306 const [ withAmount , setWithAmount ] = useState ( "" ) ;
306307 const [ withDisclaimer , setWithDisclaimer ] = useState ( false ) ;
308+ const [ tokenTab , setTokenTab ] = useState ( 0 ) ;
309+ const canTransferSub = isAdmin || isPA || role === MemberRoleEnum . TeamLeader ;
307310 const isMember = role > 0 || isAdmin ;
308311
309312 const paWalletValid = ! paWallet || isValidAddress ( paWallet ) ;
@@ -487,23 +490,31 @@ function ProgramDetail({ programId }: { programId: number }) {
487490 { /* Token Actions */ }
488491 { isMember && program . active && (
489492 < Paper sx = { { p : 2 , mb : 3 } } >
490- < Typography variant = "h6" gutterBottom > Token Actions</ Typography >
491- { walletBalance != null && (
492- < Typography variant = "body2" color = "text.secondary" sx = { { mb : 2 } } >
493- Wallet Balance: { formatFula ( walletBalance ) } FULA
494- </ Typography >
495- ) }
496- < Grid container spacing = { 3 } >
497- { /* Deposit */ }
498- < Grid item xs = { 12 } sm = { 6 } md = { 3 } >
499- < Typography variant = "subtitle2" gutterBottom > Deposit</ Typography >
493+ < Box sx = { { display : "flex" , justifyContent : "space-between" , alignItems : "center" , mb : 1 } } >
494+ < Typography variant = "h6" > Token Actions</ Typography >
495+ { walletBalance != null && (
496+ < Typography variant = "body2" color = "text.secondary" >
497+ Wallet: { formatFula ( walletBalance ) } FULA
498+ </ Typography >
499+ ) }
500+ </ Box >
501+ < Tabs value = { tokenTab } onChange = { ( _ , v ) => setTokenTab ( v ) } variant = "scrollable" scrollButtons = "auto" >
502+ < Tab label = "Deposit" />
503+ { canTransferSub && < Tab label = "Transfer to Sub-Member" /> }
504+ < Tab label = "Transfer to Parent" />
505+ < Tab label = "Withdraw" />
506+ </ Tabs >
507+
508+ { /* Deposit */ }
509+ { tokenTab === 0 && (
510+ < Box sx = { { pt : 2 , maxWidth : 480 } } >
500511 < TextField label = "Amount (FULA)" value = { depAmount } onChange = { ( e ) => setDepAmount ( e . target . value ) }
501512 fullWidth size = "small" type = "number" />
502513 < TextField label = "Note (max 128)" value = { depNote }
503514 onChange = { ( e ) => setDepNote ( e . target . value . slice ( 0 , 128 ) ) }
504515 fullWidth size = "small" sx = { { mt : 1 } } inputProps = { { maxLength : 128 } } />
505516 < OnChainDisclaimer accepted = { depDisclaimer } onChange = { setDepDisclaimer } />
506- < Button size = "small" variant = "contained" fullWidth sx = { { mt : 1 } }
517+ < Button variant = "contained" fullWidth sx = { { mt : 1 } }
507518 onClick = { ( ) => depositTokens ( programId , depAmount , 0 , depNote ) }
508519 disabled = { isDepPending || ! depAmount || ! depDisclaimer } >
509520 { isDepApproving ? < > < CircularProgress size = { 16 } sx = { { mr : 0.5 } } /> Approving...</ >
@@ -512,43 +523,43 @@ function ProgramDetail({ programId }: { programId: number }) {
512523 </ Button >
513524 { depSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Deposited!</ Alert > }
514525 { depError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( depError ) } </ Alert > }
515- </ Grid >
526+ </ Box >
527+ ) }
516528
517- { /* Transfer to Sub-Member */ }
518- { ( isAdmin || isPA || role === MemberRoleEnum . TeamLeader ) && (
519- < Grid item xs = { 12 } sm = { 6 } md = { 3 } >
520- < Typography variant = "subtitle2" gutterBottom > Transfer to Sub-Member</ Typography >
521- < TextField label = "Recipient Wallet" value = { transTo } onChange = { ( e ) => setTransTo ( e . target . value ) }
522- fullWidth size = "small" error = { ! ! transTo && ! isValidAddress ( transTo ) } />
523- < TextField label = "Amount (FULA)" value = { transAmount } onChange = { ( e ) => setTransAmount ( e . target . value ) }
529+ { /* Transfer to Sub-Member */ }
530+ { canTransferSub && tokenTab === 1 && (
531+ < Box sx = { { pt : 2 , maxWidth : 480 } } >
532+ < TextField label = "Recipient Wallet" value = { transTo } onChange = { ( e ) => setTransTo ( e . target . value ) }
533+ fullWidth size = "small" error = { ! ! transTo && ! isValidAddress ( transTo ) } />
534+ < TextField label = "Amount (FULA)" value = { transAmount } onChange = { ( e ) => setTransAmount ( e . target . value ) }
535+ fullWidth size = "small" type = "number" sx = { { mt : 1 } } />
536+ < FormControl fullWidth size = "small" sx = { { mt : 1 } } >
537+ < InputLabel > Lock</ InputLabel >
538+ < Select value = { transLocked ? "locked" : "unlocked" }
539+ onChange = { ( e ) => setTransLocked ( e . target . value === "locked" ) } label = "Lock" >
540+ < MenuItem value = "locked" > Permanently Locked</ MenuItem >
541+ < MenuItem value = "unlocked" > Unlocked / Time-locked</ MenuItem >
542+ </ Select >
543+ </ FormControl >
544+ { ! transLocked && (
545+ < TextField label = "Lock Days (0 = unlocked)" value = { transLockDays }
546+ onChange = { ( e ) => setTransLockDays ( e . target . value ) }
524547 fullWidth size = "small" type = "number" sx = { { mt : 1 } } />
525- < FormControl fullWidth size = "small" sx = { { mt : 1 } } >
526- < InputLabel > Lock</ InputLabel >
527- < Select value = { transLocked ? "locked" : "unlocked" }
528- onChange = { ( e ) => setTransLocked ( e . target . value === "locked" ) } label = "Lock" >
529- < MenuItem value = "locked" > Permanently Locked</ MenuItem >
530- < MenuItem value = "unlocked" > Unlocked / Time-locked</ MenuItem >
531- </ Select >
532- </ FormControl >
533- { ! transLocked && (
534- < TextField label = "Lock Days (0 = unlocked)" value = { transLockDays }
535- onChange = { ( e ) => setTransLockDays ( e . target . value ) }
536- fullWidth size = "small" type = "number" sx = { { mt : 1 } } />
537- ) }
538- < OnChainDisclaimer accepted = { transDisclaimer } onChange = { setTransDisclaimer } />
539- < Button size = "small" variant = "contained" fullWidth sx = { { mt : 1 } }
540- onClick = { ( ) => transfer ( programId , transTo as `0x${string } `, transAmount , transLocked , parseInt ( transLockDays ) || 0 ) }
541- disabled = { isTransPending || isTransConf || ! transTo || ! transAmount || ! isValidAddress ( transTo ) || ! transDisclaimer } >
542- { isTransPending || isTransConf ? < CircularProgress size = { 16 } /> : "Transfer" }
543- </ Button >
544- { transSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Transferred!</ Alert > }
545- { transError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( transError ) } </ Alert > }
546- </ Grid >
547- ) }
548+ ) }
549+ < OnChainDisclaimer accepted = { transDisclaimer } onChange = { setTransDisclaimer } />
550+ < Button variant = "contained" fullWidth sx = { { mt : 1 } }
551+ onClick = { ( ) => transfer ( programId , transTo as `0x${string } `, transAmount , transLocked , parseInt ( transLockDays ) || 0 ) }
552+ disabled = { isTransPending || isTransConf || ! transTo || ! transAmount || ! isValidAddress ( transTo ) || ! transDisclaimer } >
553+ { isTransPending || isTransConf ? < CircularProgress size = { 16 } /> : "Transfer" }
554+ </ Button >
555+ { transSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Transferred!</ Alert > }
556+ { transError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( transError ) } </ Alert > }
557+ </ Box >
558+ ) }
548559
549- { /* Transfer to Parent */ }
550- < Grid item xs = { 12 } sm = { 6 } md = { 3 } >
551- < Typography variant = "subtitle2" gutterBottom > Transfer to Parent </ Typography >
560+ { /* Transfer to Parent */ }
561+ { tokenTab === ( canTransferSub ? 2 : 1 ) && (
562+ < Box sx = { { pt : 2 , maxWidth : 480 } } >
552563 { transferLimit != null && Number ( transferLimit ) > 0 && (
553564 < Alert severity = "info" sx = { { mb : 1 } } > Limit: { String ( transferLimit ) } % of total balance</ Alert >
554565 ) }
@@ -558,30 +569,31 @@ function ProgramDetail({ programId }: { programId: number }) {
558569 < TextField label = "Amount (FULA)" value = { parentAmount } onChange = { ( e ) => setParentAmount ( e . target . value ) }
559570 fullWidth size = "small" type = "number" sx = { { mt : 1 } } />
560571 < OnChainDisclaimer accepted = { parentDisclaimer } onChange = { setParentDisclaimer } />
561- < Button size = "small" variant = "contained" fullWidth sx = { { mt : 1 } }
572+ < Button variant = "contained" fullWidth sx = { { mt : 1 } }
562573 onClick = { ( ) => transferBack ( programId , ( parentTo || "0x0000000000000000000000000000000000000000" ) as `0x${string } `, parentAmount ) }
563574 disabled = { isTransBackPending || isTransBackConf || ! parentAmount || ! parentDisclaimer } >
564575 { isTransBackPending || isTransBackConf ? < CircularProgress size = { 16 } /> : "Transfer to Parent" }
565576 </ Button >
566577 { transBackSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Transferred!</ Alert > }
567578 { transBackError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( transBackError ) } </ Alert > }
568- </ Grid >
579+ </ Box >
580+ ) }
569581
570- { /* Withdraw */ }
571- < Grid item xs = { 12 } sm = { 6 } md = { 3 } >
572- < Typography variant = "subtitle2" gutterBottom > Withdraw </ Typography >
582+ { /* Withdraw */ }
583+ { tokenTab === ( canTransferSub ? 3 : 2 ) && (
584+ < Box sx = { { pt : 2 , maxWidth : 480 } } >
573585 < TextField label = "Amount (FULA)" value = { withAmount } onChange = { ( e ) => setWithAmount ( e . target . value ) }
574586 fullWidth size = "small" type = "number" />
575587 < OnChainDisclaimer accepted = { withDisclaimer } onChange = { setWithDisclaimer } />
576- < Button size = "small" variant = "contained" fullWidth sx = { { mt : 1 } }
588+ < Button variant = "contained" fullWidth sx = { { mt : 1 } }
577589 onClick = { ( ) => withdraw ( programId , withAmount ) }
578590 disabled = { isWithPending || isWithConf || ! withAmount || ! withDisclaimer } >
579591 { isWithPending || isWithConf ? < CircularProgress size = { 16 } /> : "Withdraw" }
580592 </ Button >
581593 { withSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Withdrawn!</ Alert > }
582594 { withError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( withError ) } </ Alert > }
583- </ Grid >
584- </ Grid >
595+ </ Box >
596+ ) }
585597 </ Paper >
586598 ) }
587599
0 commit comments