KAGE Stake Frontend Development Guide
info
This guide covers the frontend implementation details, component architecture, and development practices for the KAGE Stake platform.
Application Overview
Component Architecture
Core Components
// StakingDashboard.tsx
export const StakingDashboard = ({
userAddress,
networkConfig,
}: StakingDashboardProps) => {
const { totalStaked, rewards, lockPeriod } = useStakingStats(userAddress);
return (
<div className="staking-dashboard">
<StakingMetrics
totalStaked={totalStaked}
rewards={rewards}
lockPeriod={lockPeriod}
/>
<StakingActions />
</div>
);
};
// StakingPoolSelector.tsx
export const StakingPoolSelector = ({
availablePools,
onPoolSelect,
}: StakingPoolProps) => {
return (
<div className="pool-grid">
{availablePools.map(pool => (
<PoolCard
key={pool.id}
poolData={pool}
onSelect={() => onPoolSelect(pool)}
APY={pool.apy}
lockupPeriod={pool.lockupDays}
/>
))}
</div>
);
};
Transaction Flow Components
// StakingFlow.tsx
export const StakingFlow = ({
selectedPool,
userBalance,
}: StakingFlowProps) => {
const [step, setStep] = useState<StakingStep>("APPROVE");
const steps = {
APPROVE: <TokenApproval />,
STAKE: <StakeConfirmation />,
CONFIRM: <StakingSuccess />,
};
return (
<StakingFlowContainer>
{steps[step]}
<StakingProgress currentStep={step} />
</StakingFlowContainer>
);
};
User Interaction Flows
1. Initial Platform Entry
2. Pool Discovery & Analysis
Component States
Pool Display States
type PoolState = {
isLoading: boolean;
isStaked: boolean;
isLocked: boolean;
canWithdraw: boolean;
hasRewards: boolean;
};
const PoolDisplay: React.FC<PoolDisplayProps> = ({ poolId }) => {
const [state, setState] = useState<PoolState>({
isLoading: true,
isStaked: false,
isLocked: false,
canWithdraw: false,
hasRewards: false,
});
useEffect(() => {
// Update state based on contract data
}, [poolId]);
return (
<div
className={cn(
"pool-card",
state.isStaked && "staked",
state.isLocked && "locked",
state.canWithdraw && "withdrawable",
)}
>
{/* Pool content */}
</div>
);
};
User Experience Enhancements
1. Loading States
const LoadingState = () => (
<div className="animate-pulse">
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
<div className="space-y-3 mt-4">
<div className="h-4 bg-gray-200 rounded"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
</div>
</div>
);
2. Error Handling
const ErrorBoundary = ({ children }: { children: React.ReactNode }) => {
const [error, setError] = useState<Error | null>(null);
if (error) {
return (
<div className="error-container">
<h3>Something went wrong</h3>
<p>{error.message}</p>
<Button onClick={() => setError(null)}>Try Again</Button>
</div>
);
}
return children;
};
3. Transaction Feedback
const TransactionStatus = ({ hash, status }: TransactionStatusProps) => {
return (
<div className="transaction-status">
<div className="status-icon">
{status === "pending" && <Spinner />}
{status === "success" && <CheckIcon />}
{status === "error" && <ErrorIcon />}
</div>
<div className="status-details">
<p>{getStatusMessage(status)}</p>
<a href={`${scanURL}/tx/${hash}`} target="_blank">
View on Explorer
</a>
</div>
</div>
);
};
Performance Optimization
1. Data Caching
const usePoolData = (poolId: number) => {
const queryClient = useQueryClient();
return useQuery({
queryKey: ["pool", poolId],
queryFn: () => fetchPoolData(poolId),
staleTime: 30000, // 30 seconds
cacheTime: 3600000, // 1 hour
});
};
2. Component Memoization
const PoolMetrics = React.memo(({ poolData }: PoolMetricsProps) => {
const metrics = useMemo(() => calculatePoolMetrics(poolData), [poolData]);
return <div className="pool-metrics">{/* Render metrics */}</div>;
});
Accessibility Features
1. Keyboard Navigation
const KeyboardNavigationHandler = () => {
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
switch (e.key) {
case "ArrowRight":
navigateToNextPool();
break;
case "ArrowLeft":
navigateToPreviousPool();
break;
case "Enter":
selectCurrentPool();
break;
case "Escape":
closeCurrentModal();
break;
}
};
window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress);
}, []);
};
2. Screen Reader Support
const PoolMetrics = ({ poolData }: PoolMetricsProps) => {
return (
<div role="region" aria-label="Pool Statistics" tabIndex={0}>
<div aria-live="polite">
<span className="sr-only">Current APY:</span>
<span aria-label={`${poolData.apy}% Annual Percentage Yield`}>
{poolData.apy}% APY
</span>
</div>
<div
role="meter"
aria-label="Pool Utilization"
aria-valuenow={poolData.utilization}
aria-valuemin={0}
aria-valuemax={100}
>
<ProgressBar
value={poolData.utilization}
className="visually-accessible"
/>
</div>
</div>
);
};
Testing
describe("Pool Integration", () => {
test("Full Staking Flow", async () => {
const { container } = render(<PoolTabs {...mockProps} />);
// Connect wallet
await userEvent.click(screen.getByText("Connect Wallet"));
// Select pool
await userEvent.click(screen.getByText("Pool #1"));
// Enter stake amount
await userEvent.type(screen.getByPlaceholderText("Amount"), "1000");
// Confirm stake
await userEvent.click(screen.getByText("Stake KAGE"));
// Verify success state
expect(screen.getByText("Staking Successful")).toBeInTheDocument();
});
});