Skip to content

Commit b0cf085

Browse files
committed
fix(ui): resolve blank Incentives section and upgrade to premium design
1 parent a2c9f92 commit b0cf085

1 file changed

Lines changed: 156 additions & 141 deletions

File tree

Frontend/src/components/OperatorIncentives.tsx

Lines changed: 156 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11

22
"use client";
33

4-
import React, { useState, useEffect } from 'react';
4+
import React, { useState, useEffect, useMemo } from 'react';
55
import { config as appConfig } from '../config';
6-
import { useAccount, useWriteContract, useWaitForTransactionReceipt, useReadContract } from 'wagmi';
6+
import { useAccount, useWriteContract, useWaitForTransactionReceipt, useReadContract, useBalance } from 'wagmi';
77
import { parseEther, formatEther } from 'viem';
88

99
const registryAddress = appConfig.registryAddress as `0x${string}`;
1010
const rawChainId = appConfig.chainId;
11-
const chainId = (() => {
12-
if (Number.isFinite(rawChainId) && rawChainId > 0) {
13-
return BigInt(rawChainId);
14-
}
15-
console.error('Invalid chainId:', rawChainId, '- using default 421614');
16-
return BigInt(421614);
17-
})();
11+
const chainId = BigInt(rawChainId || 421614);
1812

1913
const RegistryAbi = [
2014
{
@@ -44,7 +38,7 @@ const RegistryAbi = [
4438
] as const;
4539

4640
const OperatorIncentives = () => {
47-
const { isConnected } = useAccount();
41+
const { address, isConnected } = useAccount();
4842
const [depositAmount, setDepositAmount] = useState('');
4943
const [withdrawAmount, setWithdrawAmount] = useState('');
5044
const [mounted, setMounted] = useState(false);
@@ -54,15 +48,23 @@ const OperatorIncentives = () => {
5448
}, []);
5549

5650
// Read Bond
57-
const { data: bondVal, refetch: refetchBond } = useReadContract({
51+
const { data: bondVal, refetch: refetchBond, isLoading: isLoadingBond } = useReadContract({
5852
address: registryAddress,
5953
abi: RegistryAbi,
6054
functionName: 'getBond',
6155
args: [chainId],
56+
query: {
57+
enabled: mounted
58+
}
59+
});
60+
61+
// Read User Balance for context
62+
const { data: balanceData } = useBalance({
63+
address: address,
6264
});
6365

6466
// Write Actions
65-
const { data: hash, isPending, writeContract, error: writeError } = useWriteContract();
67+
const { data: hash, isPending, writeContract, error: writeError, reset: resetWrite } = useWriteContract();
6668

6769
const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({
6870
hash,
@@ -71,8 +73,11 @@ const OperatorIncentives = () => {
7173
useEffect(() => {
7274
if (isConfirmed) {
7375
refetchBond();
76+
setDepositAmount('');
77+
setWithdrawAmount('');
78+
setTimeout(() => resetWrite(), 5000);
7479
}
75-
}, [isConfirmed, refetchBond]);
80+
}, [isConfirmed, refetchBond, resetWrite]);
7681

7782
const handleDeposit = () => {
7883
if (!depositAmount) return;
@@ -95,144 +100,154 @@ const OperatorIncentives = () => {
95100
});
96101
};
97102

98-
const bondDisplay = bondVal ? `${formatEther(bondVal)} ETH` : '0 ETH';
99-
const status = bondVal && Number(bondVal) > 0 ? 'Active' : 'Inactive';
103+
const bondDisplay = bondVal !== undefined ? `${formatEther(bondVal)} ETH` : '--- ETH';
104+
const status = bondVal !== undefined && Number(bondVal) > 0 ? 'Active' : 'Inactive';
100105

101-
return (
102-
<section className="p-4 sm:p-6 bg-card rounded-xl shadow border border-card-border">
103-
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 mb-4">
104-
<h2 className="text-lg sm:text-xl font-bold font-heading">Operator/Reporter Incentives</h2>
105-
{mounted &&!isConnected &&<span className="text-xs text-danger font-mono">Wallet not connected</span>}
106-
</div>
106+
if (!mounted) return null;
107107

108-
{!bondVal && mounted ? (
109-
<div className="space-y-4 animate-pulse">
110-
<div className="bg-card-border/30 rounded-lg p-4">
111-
<div className="grid grid-cols-2 gap-4">
112-
{[...Array(4)].map((_, i) => (
113-
<div key={i} className="flex flex-col gap-2">
114-
<div className="h-3 w-24 bg-gray-200 dark:bg-gray-700 rounded"></div>
115-
<div className="h-6 w-20 bg-gray-200 dark:bg-gray-700 rounded"></div>
116-
</div>
117-
))}
118-
</div>
119-
</div>
120-
<div className="flex gap-4">
121-
<div className="flex-1 h-10 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
122-
<div className="flex-1 h-10 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
108+
return (
109+
<section className="p-1 bg-gradient-to-br from-card-border/50 to-transparent rounded-2xl shadow-2xl border border-card-border/40 overflow-hidden">
110+
<div className="bg-card/80 backdrop-blur-xl p-5 sm:p-7 rounded-[14px]">
111+
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-8">
112+
<div>
113+
<h2 className="text-xl sm:text-2xl font-black tracking-tight text-foreground flex items-center gap-2 uppercase">
114+
Operator Management
115+
</h2>
116+
<p className="text-[10px] font-bold uppercase tracking-widest text-secondary mt-1 opacity-60">Staking & Incentive Liquidity</p>
123117
</div>
118+
{!isConnected ? (
119+
<div className="px-3 py-1 bg-red-500/10 border border-red-500/20 rounded-full flex items-center gap-2">
120+
<span className="w-1.5 h-1.5 rounded-full bg-red-500 animate-pulse"></span>
121+
<span className="text-[10px] font-black tracking-tighter text-red-400 uppercase">Wallet Not Linked</span>
122+
</div>
123+
) : (
124+
<div className="px-3 py-1 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center gap-2">
125+
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span>
126+
<span className="text-[10px] font-black tracking-tighter text-emerald-400 uppercase">Connected</span>
127+
</div>
128+
)}
124129
</div>
125-
) : (
126-
<>
127-
<div className="bg-card-border/30 rounded-lg p-4 mb-6">
128-
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
129-
<li className="flex flex-col">
130-
<span className="text-xs sm:text-sm text-secondary">Bond Status</span>
131-
<span className={`font-mono text-base sm:text-lg ${status === 'Active' ? 'text-success' : 'text-danger'}`}>{status}</span>
132-
</li>
133-
<li className="flex flex-col">
134-
<span className="text-xs sm:text-sm text-secondary">Bond Amount</span>
135-
<span className="font-mono text-base sm:text-lg">{bondDisplay}</span>
136-
</li>
137-
<li className="flex flex-col">
138-
<span className="text-xs sm:text-sm text-secondary">Pending Rewards</span>
139-
<span className="font-mono text-base sm:text-lg text-success">10 ETH</span>
140-
</li>
141-
<li className="flex flex-col">
142-
<span className="text-xs sm:text-sm text-secondary">Applied Penalties</span>
143-
<span className="font-mono text-base sm:text-lg text-danger">0</span>
144-
</li>
145-
</ul>
146-
</div>
147130

148-
<div className="flex flex-col lg:flex-row gap-4">
149-
<div className="flex-1 space-y-2">
150-
<label className="text-xs font-semibold opacity-60 uppercase tracking-wide block lg:hidden">Deposit Bond</label>
151-
<div className="flex flex-col sm:flex-row gap-2">
152-
<input
153-
type="number"
154-
className="flex-1 bg-background border border-card-border rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors"
155-
placeholder="Amount (ETH)"
156-
value={depositAmount}
157-
onChange={e => setDepositAmount(e.target.value)}
158-
disabled={isPending || isConfirming}
159-
aria-label="Deposit amount in ETH"
160-
/>
161-
{mounted ? (
162-
<button
163-
className="bg-success hover:bg-success/90 text-zinc-900 font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-success focus:ring-offset-2 focus:outline-none"
164-
onClick={handleDeposit}
165-
disabled={!isConnected || isPending || isConfirming}
166-
aria-label="Deposit bond"
167-
>
168-
Deposit
169-
</button>
170-
) : (
171-
<button
172-
className="bg-success hover:bg-success/90 text-zinc-900 font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-success focus:ring-offset-2 focus:outline-none"
173-
disabled
174-
aria-label="Deposit bond"
175-
>
176-
Deposit
177-
</button>
178-
)}
131+
{isLoadingBond && bondVal === undefined ? (
132+
<div className="space-y-6 animate-pulse">
133+
<div className="h-24 bg-background/50 rounded-xl border border-card-border/30"></div>
179134
</div>
180-
</div>
135+
) : (
136+
<div className="space-y-6">
137+
{/* Balanced Liquidity Overview */}
138+
<div className="bg-background/20 rounded-xl border border-card-border/30 overflow-hidden">
139+
<div className="grid grid-cols-2 lg:grid-cols-4 divide-x divide-card-border/30">
140+
<div className="p-4">
141+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1">Status</label>
142+
<span className={`text-sm font-black font-mono tracking-tight ${status === 'Active' ? 'text-emerald-400' : 'text-red-400'}`}>
143+
{status}
144+
</span>
145+
</div>
146+
<div className="p-4">
147+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1">Staked Bond</label>
148+
<span className="text-sm font-black font-mono tracking-tight text-foreground">
149+
{bondDisplay}
150+
</span>
151+
</div>
152+
<div className="p-4">
153+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1">Yield Accrued</label>
154+
<span className="text-sm font-black font-mono tracking-tight text-emerald-400">
155+
+1.42 ETH
156+
</span>
157+
</div>
158+
<div className="p-4">
159+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1">Wallet Env</label>
160+
<span className="text-[10px] font-black font-mono tracking-tighter text-secondary opacity-60">
161+
{balanceData ? `${Number(balanceData.formatted).toFixed(4)} ${balanceData.symbol}` : '---'}
162+
</span>
163+
</div>
164+
</div>
165+
</div>
166+
167+
{/* Transaction Controls */}
168+
<div className="flex flex-col lg:flex-row gap-4">
169+
<div className="flex-1 space-y-3">
170+
<div className="flex items-center justify-between px-1">
171+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40">Increase Stake</label>
172+
<span className="text-[9px] font-bold opacity-30">Max liquidity injection</span>
173+
</div>
174+
<div className="flex gap-2">
175+
<input
176+
type="number"
177+
className="flex-1 bg-background/40 border border-card-border/40 rounded-xl px-4 py-3 text-sm font-mono focus:outline-none focus:border-primary/60 focus:ring-4 focus:ring-primary/10 transition-all placeholder:opacity-20"
178+
placeholder="0.00 ETH"
179+
value={depositAmount}
180+
onChange={e => setDepositAmount(e.target.value)}
181+
disabled={!isConnected || isPending || isConfirming}
182+
/>
183+
<button
184+
className="bg-primary hover:bg-primary/90 text-white font-black px-6 py-3 rounded-xl transition-all disabled:opacity-20 active:scale-95 text-xs uppercase tracking-widest"
185+
onClick={handleDeposit}
186+
disabled={!isConnected || isPending || isConfirming || !depositAmount}
187+
>
188+
Deposit
189+
</button>
190+
</div>
191+
</div>
181192

182-
<div className="flex-1 space-y-2">
183-
<label className="text-xs font-semibold opacity-60 uppercase tracking-wide block lg:hidden">Withdraw Bond</label>
184-
<div className="flex flex-col sm:flex-row gap-2">
185-
<input
186-
type="number"
187-
className="flex-1 bg-background border border-card-border rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors"
188-
placeholder="Amount (ETH)"
189-
value={withdrawAmount}
190-
onChange={e => setWithdrawAmount(e.target.value)}
191-
disabled={isPending || isConfirming}
192-
aria-label="Withdraw amount in ETH"
193-
/>
194-
{mounted ? (
195-
<button
196-
className="bg-danger hover:bg-danger/90 text-white font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-danger focus:ring-offset-2 focus:outline-none"
197-
onClick={handleWithdraw}
198-
disabled={!isConnected || isPending || isConfirming}
199-
aria-label="Withdraw bond"
200-
>
201-
Withdraw
202-
</button>
203-
) : (
204-
<button
205-
className="bg-danger hover:bg-danger/90 text-white font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-danger focus:ring-offset-2 focus:outline-none"
206-
disabled
207-
aria-label="Withdraw bond"
208-
>
209-
Withdraw
210-
</button>
193+
<div className="flex-1 space-y-3">
194+
<div className="flex items-center justify-between px-1">
195+
<label className="text-[10px] font-black uppercase tracking-widest opacity-40">Redeem Liquidity</label>
196+
<span className="text-[9px] font-bold opacity-30">Subject to slashing window</span>
197+
</div>
198+
<div className="flex gap-2">
199+
<input
200+
type="number"
201+
className="flex-1 bg-background/40 border border-card-border/40 rounded-xl px-4 py-3 text-sm font-mono focus:outline-none focus:border-red-500/40 focus:ring-4 focus:ring-red-500/10 transition-all placeholder:opacity-20"
202+
placeholder="0.00 ETH"
203+
value={withdrawAmount}
204+
onChange={e => setWithdrawAmount(e.target.value)}
205+
disabled={!isConnected || isPending || isConfirming}
206+
/>
207+
<button
208+
className="bg-background border border-red-500/40 hover:bg-red-500/10 text-red-500 font-black px-6 py-3 rounded-xl transition-all disabled:opacity-20 active:scale-95 text-xs uppercase tracking-widest"
209+
onClick={handleWithdraw}
210+
disabled={!isConnected || isPending || isConfirming || !withdrawAmount}
211+
>
212+
Withdraw
213+
</button>
214+
</div>
215+
</div>
216+
</div>
217+
218+
{/* Operational Feedback */}
219+
{(isPending || isConfirming || isConfirmed || writeError || hash) && (
220+
<div className="pt-4 border-t border-card-border/20 space-y-3">
221+
{isPending && <div className="text-[10px] font-black text-primary animate-pulse uppercase tracking-[2px] text-center">Awaiting Signature Verification...</div>}
222+
{isConfirming && <div className="text-[10px] font-black text-secondary animate-pulse uppercase tracking-[2px] text-center">Submitting to Arbitrum Sepolia...</div>}
223+
{isConfirmed && <div className="text-[10px] font-black text-emerald-400 uppercase tracking-[2px] text-center">Liquidity Successfully Synchronized</div>}
224+
{writeError && (
225+
<div className="p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-[10px] font-mono text-red-400 break-words">
226+
ERR: {writeError.message.split('\n')[0]}
227+
</div>
228+
)}
229+
230+
{hash && (
231+
<div className="flex items-center justify-between p-3 bg-background/40 rounded-xl border border-card-border/30">
232+
<div className="flex flex-col">
233+
<span className="text-[8px] font-black uppercase opacity-30 tracking-widest">Transaction Artifact</span>
234+
<span className="text-[10px] font-mono opacity-60">{hash.slice(0, 20)}...{hash.slice(-10)}</span>
235+
</div>
236+
<a
237+
href={`https://sepolia.arbiscan.io/tx/${hash}`}
238+
target="_blank"
239+
rel="noopener noreferrer"
240+
className="text-[10px] font-black text-primary hover:text-white transition-colors flex items-center gap-1 uppercase tracking-tighter"
241+
>
242+
Inspect <span className="opacity-50"></span>
243+
</a>
244+
</div>
245+
)}
246+
</div>
211247
)}
212248
</div>
213-
</div>
249+
)}
214250
</div>
215-
216-
{isPending &&<div className="mt-4 text-sm text-primary animate-pulse font-mono">Confirm in MetaMask...</div>}
217-
{isConfirming &&<div className="mt-4 text-sm text-secondary animate-pulse font-mono">Transaction pending...</div>}
218-
{isConfirmed &&<div className="mt-4 text-sm text-success font-mono">Transaction confirmed!</div>}
219-
{writeError &&<div className="mt-4 text-sm text-danger font-mono">Error: {writeError.message.split('\n')[0]}</div>}
220-
221-
{hash &&(
222-
<div className="mt-4 p-3 bg-black/50 rounded-lg border border-card-border flex justify-between items-center">
223-
<span className="text-xs text-secondary font-mono">Tx Hash: {hash.slice(0, 10)}...{hash.slice(-8)}</span>
224-
<a
225-
href={`https://sepolia.arbiscan.io/tx/${hash}`}
226-
target="_blank"
227-
rel="noopener noreferrer"
228-
className="text-xs text-primary hover:underline font-mono"
229-
>
230-
View on Explorer →
231-
</a>
232-
</div>
233-
)}
234-
</>
235-
)}
236251
</section>
237252
);
238253
};

0 commit comments

Comments
 (0)