Spaces:
Running
Running
| import React, { useState } from 'react'; | |
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Input } from '@/components/ui/input'; | |
| import { Label } from '@/components/ui/label'; | |
| import { Badge } from '@/components/ui/badge'; | |
| import { Plus, Trash2, Target, Activity, DollarSign } from 'lucide-react'; | |
| import { useToast } from '@/hooks/use-toast'; | |
| interface TargetWalletManagerProps { | |
| wallets: string[]; | |
| setWallets: (wallets: string[]) => void; | |
| } | |
| export const TargetWalletManager: React.FC<TargetWalletManagerProps> = ({ | |
| wallets, | |
| setWallets | |
| }) => { | |
| const [newWallet, setNewWallet] = useState(''); | |
| const { toast } = useToast(); | |
| const addWallet = () => { | |
| if (!newWallet.trim()) { | |
| toast({ | |
| title: "Invalid Address", | |
| description: "Please enter a valid Solana wallet address", | |
| variant: "destructive" | |
| }); | |
| return; | |
| } | |
| // Basic validation for Solana address (should be 32-44 characters) | |
| if (newWallet.length < 32 || newWallet.length > 44) { | |
| toast({ | |
| title: "Invalid Address Format", | |
| description: "Solana addresses should be 32-44 characters long", | |
| variant: "destructive" | |
| }); | |
| return; | |
| } | |
| if (wallets.includes(newWallet)) { | |
| toast({ | |
| title: "Wallet Already Added", | |
| description: "This wallet address is already in your target list", | |
| variant: "destructive" | |
| }); | |
| return; | |
| } | |
| setWallets([...wallets, newWallet]); | |
| setNewWallet(''); | |
| toast({ | |
| title: "Wallet Added", | |
| description: "Target wallet successfully added to monitoring list", | |
| }); | |
| }; | |
| const removeWallet = (wallet: string) => { | |
| setWallets(wallets.filter(w => w !== wallet)); | |
| toast({ | |
| title: "Wallet Removed", | |
| description: "Target wallet removed from monitoring list", | |
| }); | |
| }; | |
| // Mock data for demonstration | |
| const walletStats = { | |
| [wallets[0]]: { | |
| totalTrades: 156, | |
| profitLoss: 2341.56, | |
| successRate: 72.4, | |
| lastActive: '2 minutes ago' | |
| } | |
| }; | |
| return ( | |
| <Card className="bg-gradient-card border-border/50"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"> | |
| <Target className="h-5 w-5 text-primary" /> | |
| Target Wallets | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="wallet-address">Add Target Wallet</Label> | |
| <div className="flex gap-2"> | |
| <Input | |
| id="wallet-address" | |
| placeholder="Enter Solana wallet address..." | |
| value={newWallet} | |
| onChange={(e) => setNewWallet(e.target.value)} | |
| className="flex-1" | |
| /> | |
| <Button onClick={addWallet} className="gap-2"> | |
| <Plus className="h-4 w-4" /> | |
| Add | |
| </Button> | |
| </div> | |
| </div> | |
| <div className="space-y-3"> | |
| {wallets.length === 0 ? ( | |
| <div className="text-center py-8 text-muted-foreground"> | |
| <Target className="h-12 w-12 mx-auto mb-3 opacity-50" /> | |
| <p>No target wallets added yet</p> | |
| <p className="text-sm">Add wallet addresses to start copying trades</p> | |
| </div> | |
| ) : ( | |
| wallets.map((wallet, index) => ( | |
| <div | |
| key={wallet} | |
| className="p-4 rounded-lg border bg-secondary/50 animate-fade-in" | |
| style={{ animationDelay: `${index * 0.1}s` }} | |
| > | |
| <div className="flex items-start justify-between mb-3"> | |
| <div className="flex-1 min-w-0"> | |
| <div className="flex items-center gap-2 mb-1"> | |
| <Badge variant="outline" className="text-xs"> | |
| #{index + 1} | |
| </Badge> | |
| <Badge variant="secondary" className="text-xs"> | |
| Active | |
| </Badge> | |
| </div> | |
| <p className="font-mono text-sm break-all"> | |
| {wallet.slice(0, 8)}...{wallet.slice(-8)} | |
| </p> | |
| </div> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={() => removeWallet(wallet)} | |
| className="text-destructive hover:text-destructive" | |
| > | |
| <Trash2 className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| {/* Mock stats for demonstration */} | |
| {walletStats[wallet] && ( | |
| <div className="grid grid-cols-2 gap-4 text-xs"> | |
| <div className="flex items-center gap-1"> | |
| <Activity className="h-3 w-3 text-info" /> | |
| <span>{walletStats[wallet].totalTrades} trades</span> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <DollarSign className="h-3 w-3 text-profit" /> | |
| <span className="text-profit"> | |
| ${walletStats[wallet].profitLoss.toFixed(2)} | |
| </span> | |
| </div> | |
| <div className="text-muted-foreground col-span-2"> | |
| Last active: {walletStats[wallet].lastActive} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| )) | |
| )} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ); | |
| }; |