@@ -4,6 +4,7 @@ import { hasCustomRpcConfig, networkConfig } from "../config/network";
44import { useVault } from "../context/VaultContext" ;
55import ApiStatusBanner from "./ApiStatusBanner" ;
66import { useToast } from "../context/ToastContext" ;
7+ import { FormField , SubmitButton , useForm , type ValidationSchema } from "../forms" ;
78
89interface VaultDashboardProps {
910 walletAddress : string | null ;
@@ -13,30 +14,52 @@ const VaultDashboard: React.FC<VaultDashboardProps> = ({ walletAddress }) => {
1314 const { formattedTvl, formattedApy, summary, error, isLoading } = useVault ( ) ;
1415 const toast = useToast ( ) ;
1516 const [ activeTab , setActiveTab ] = useState < "deposit" | "withdraw" > ( "deposit" ) ;
16- const [ amount , setAmount ] = useState ( "" ) ;
1717 const [ isProcessing , setIsProcessing ] = useState ( false ) ;
1818 const [ fakeBalance , setFakeBalance ] = useState ( 1250.5 ) ;
1919
2020 const yieldRate = formattedApy ;
2121 const tvl = formattedTvl ;
2222 const strategy = summary . strategy ;
2323
24- const handleTransaction = ( ) => {
25- if ( ! walletAddress || ! amount || isNaN ( Number ( amount ) ) ) {
24+ const schema : ValidationSchema < { amount : string } > = {
25+ amount : {
26+ required : "Enter an amount to continue." ,
27+ custom : ( value ) => {
28+ const parsed = Number ( value ) ;
29+ if ( Number . isNaN ( parsed ) ) {
30+ return "Enter a valid number." ;
31+ }
32+ if ( parsed <= 0 ) {
33+ return "Amount must be greater than 0." ;
34+ }
35+ return undefined ;
36+ } ,
37+ } ,
38+ } ;
39+
40+ const { values, errors, handleChange, handleBlur, handleSubmit } = useForm (
41+ { amount : "" } ,
42+ schema ,
43+ ) ;
44+
45+ const handleTransaction = async ( ) => {
46+ if ( ! walletAddress ) {
2647 toast . warning ( {
27- title : "Enter a valid amount " ,
28- description : "Choose a wallet and amount before submitting the transaction." ,
48+ title : "Wallet required " ,
49+ description : "Connect your wallet before submitting a transaction." ,
2950 } ) ;
3051 return ;
3152 }
53+
3254 setIsProcessing ( true ) ;
3355
3456 // Simulate transaction delay
35- setTimeout ( ( ) => {
36- const value = Number ( amount ) ;
57+ await new Promise < void > ( ( resolve ) => {
58+ setTimeout ( ( ) => {
59+ const value = Number ( values . amount ) ;
3760 if ( activeTab === "deposit" ) setFakeBalance ( prev => prev + value ) ;
3861 if ( activeTab === "withdraw" ) setFakeBalance ( prev => Math . max ( 0 , prev - value ) ) ;
39- setAmount ( "" ) ;
62+ handleChange ( { target : { name : "amount" , value : "" } } as Parameters < typeof handleChange > [ 0 ] ) ;
4063 setIsProcessing ( false ) ;
4164 toast . success ( {
4265 title : activeTab === "deposit" ? "Deposit queued" : "Withdrawal queued" ,
@@ -45,7 +68,9 @@ const VaultDashboard: React.FC<VaultDashboardProps> = ({ walletAddress }) => {
4568 ? `${ value . toFixed ( 2 ) } USDC has been added to your pending vault activity.`
4669 : `${ value . toFixed ( 2 ) } USDC has been added to your pending withdrawal activity.` ,
4770 } ) ;
48- } , 2000 ) ;
71+ resolve ( ) ;
72+ } , 2000 ) ;
73+ } ) ;
4974 } ;
5075
5176 return (
@@ -188,24 +213,32 @@ const VaultDashboard: React.FC<VaultDashboardProps> = ({ walletAddress }) => {
188213
189214 < div className = "flex justify-between items-center" style = { { marginBottom : '16px' } } >
190215 < div style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } >
191- { activeTab === 'deposit' ? 'Amount to deposit' : 'Amount to withdraw' }
216+ Transaction
192217 </ div >
193218 < div style = { { color : 'var(--text-secondary)' , fontSize : '0.85rem' } } >
194219 Balance: < span style = { { color : 'var(--text-primary)' , fontWeight : 600 } } > { walletAddress ? fakeBalance . toFixed ( 2 ) : '0.00' } </ span >
195220 </ div >
196221 </ div >
197222
198- < div className = "input-group" style = { { marginBottom : '24px' } } >
199- < div className = "input-wrapper" >
200- < span style = { { color : 'var(--text-secondary)' , paddingRight : '12px' , borderRight : '1px solid var(--border-glass)' , marginRight : '16px' } } > USDC </ span >
201- < input
202- className = "input-field "
223+ < form onSubmit = { handleSubmit ( handleTransaction ) } >
224+ < div className = "input-group" style = { { marginBottom : '24px' } } >
225+ < FormField
226+ label = { activeTab === 'deposit' ? 'Amount to deposit' : 'Amount to withdraw' }
227+ name = "amount "
203228 type = "number"
204229 placeholder = "0.00"
205- value = { amount }
206- onChange = { ( e ) => setAmount ( e . target . value ) }
230+ value = { values . amount }
231+ onChange = { handleChange }
232+ onBlur = { handleBlur }
233+ error = { errors . amount }
234+ style = { { fontSize : '1.25rem' , fontFamily : 'var(--font-display)' } }
207235 />
236+ </ div >
237+
238+ < div className = "flex justify-between" style = { { marginBottom : '24px' } } >
239+ < span style = { { color : 'var(--text-secondary)' , fontSize : '0.85rem' } } > Asset: USDC</ span >
208240 < button
241+ type = "button"
209242 style = { {
210243 color : 'var(--accent-cyan)' ,
211244 fontSize : '0.8rem' ,
@@ -214,40 +247,38 @@ const VaultDashboard: React.FC<VaultDashboardProps> = ({ walletAddress }) => {
214247 padding : '4px 10px' ,
215248 borderRadius : '6px'
216249 } }
217- onClick = { ( ) => setAmount ( fakeBalance . toString ( ) ) }
250+ onClick = { ( ) => handleChange ( { target : { name : 'amount' , value : fakeBalance . toString ( ) } } as Parameters < typeof handleChange > [ 0 ] ) }
218251 >
219252 MAX
220253 </ button >
221254 </ div >
222- </ div >
223255
224- < div className = "glass-panel" style = { { padding : '16px' , background : 'var(--bg-muted)' , marginBottom : '24px' } } >
225- < div className = "flex justify-between items-center" >
226- < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > BENJI Strategy</ span >
227- < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } >
228- { strategy . status === 'active' ? 'Active' : 'Inactive' }
229- </ span >
230- </ div >
231- < div className = "flex justify-between items-center" style = { { marginTop : '8px' } } >
232- < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > Exchange Rate</ span >
233- < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } >
234- 1 yvUSDC = { summary . exchangeRate . toFixed ( 3 ) } USDC
235- </ span >
236- </ div >
237- < div className = "flex justify-between items-center" style = { { marginTop : '8px' } } >
238- < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > Network Fee</ span >
239- < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } > { summary . networkFeeEstimate } </ span >
256+ < div className = "glass-panel" style = { { padding : '16px' , background : 'var(--bg-muted)' , marginBottom : '24px' } } >
257+ < div className = "flex justify-between items-center" >
258+ < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > BENJI Strategy</ span >
259+ < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } >
260+ { strategy . status === 'active' ? 'Active' : 'Inactive' }
261+ </ span >
262+ </ div >
263+ < div className = "flex justify-between items-center" style = { { marginTop : '8px' } } >
264+ < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > Exchange Rate</ span >
265+ < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } >
266+ 1 yvUSDC = { summary . exchangeRate . toFixed ( 3 ) } USDC
267+ </ span >
268+ </ div >
269+ < div className = "flex justify-between items-center" style = { { marginTop : '8px' } } >
270+ < span style = { { color : 'var(--text-secondary)' , fontSize : '0.9rem' } } > Network Fee</ span >
271+ < span style = { { fontSize : '0.9rem' , fontWeight : 500 } } > { summary . networkFeeEstimate } </ span >
272+ </ div >
240273 </ div >
241- </ div >
242274
243- < button
244- className = "btn btn-primary"
245- style = { { width : '100%' , padding : '16px' , fontSize : '1.1rem' } }
246- onClick = { handleTransaction }
247- disabled = { isProcessing || ! amount || Number ( amount ) <= 0 }
248- >
249- { isProcessing ? 'Processing Transaction...' : ( activeTab === 'deposit' ? 'Approve & Deposit' : 'Withdraw Funds' ) }
250- </ button >
275+ < SubmitButton
276+ loading = { isProcessing }
277+ disabled = { ! values . amount || Number ( values . amount ) <= 0 }
278+ label = { activeTab === 'deposit' ? 'Approve & Deposit' : 'Withdraw Funds' }
279+ loadingLabel = "Processing Transaction..."
280+ />
281+ </ form >
251282
252283 </ div >
253284 </ div >
0 commit comments