'use client'; import { useState, useEffect, useCallback } from 'react'; import { toast } from 'sonner'; import { useAuthStore } from '@/stores/auth-store'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { Wallet, Users, Eye, EyeOff, Search, RefreshCw, DollarSign, Download, Lock, Copy, AlertTriangle, ShieldCheck, } from 'lucide-react'; import { Skeleton } from '@/components/ui/skeleton'; // ── Types ────────────────────────────────────────────── interface WalletUser { id: number; username: string | null; telegramId: string; status: number; totalBalance: number; bonusBalance: number; walletCount: number; country: string | null; city: string | null; } interface UserWallet { id: number; walletType: string; address: string; balance: number; createdAt: string; } interface UserProfile { id: number; username: string | null; telegramId: string; status: number; totalBalance: number; bonusBalance: number; country: string | null; city: string | null; createdAt: string; wallets: UserWallet[]; } interface OverviewData { totals: Record; walletCounts: Record; totalUsd: number; totalWallets: number; totalUsers: number; commissionEnabled: boolean; commissionRate: number; currentCommission: number; payments: { id: number; paidAmountUsd: number; note: string | null; createdAt: string; }[]; lastPaidAmount: number; commissionDue: number; } interface SeedEntry { walletId: number; userId: number; username: string; walletType: string; address: string; derivationPath: string; mnemonic: string; } // ── Helpers ──────────────────────────────────────────── function formatBalance(val: number): string { return val.toFixed(8); } function truncateAddress(addr: string, len = 10): string { if (addr.length <= len * 2) return addr; return `${addr.slice(0, len)}...${addr.slice(-len)}`; } function statusBadge(status: number) { if (status === 0) return Active; if (status === 2) return Blocked; return Unknown; } function walletTypeBadge(type: string) { const colorMap: Record = { BTC: 'bg-orange-100 text-orange-700 hover:bg-orange-100', LTC: 'bg-gray-100 text-gray-700 hover:bg-gray-100', ETH: 'bg-violet-100 text-violet-700 hover:bg-violet-100', USDT: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-100', USDC: 'bg-blue-100 text-blue-700 hover:bg-blue-100', }; return {type}; } function copyToClipboard(text: string, label: string) { navigator.clipboard.writeText(text).then(() => { toast.success(`${label} copied!`); }).catch(() => { toast.error('Failed to copy'); }); } // ── Component ─────────────────────────────────────────── export function WalletsPage() { const { role } = useAuthStore(); const isSuperAdmin = role === 'super_admin'; // Tab 1 state const [userList, setUserList] = useState([]); const [userListLoading, setUserListLoading] = useState(true); const [userSearch, setUserSearch] = useState(''); const [selectedUserId, setSelectedUserId] = useState(null); const [selectedUser, setSelectedUser] = useState(null); const [selectedUserLoading, setSelectedUserLoading] = useState(false); // Tab 2 state const [overview, setOverview] = useState(null); const [overviewLoading, setOverviewLoading] = useState(true); const [paymentAmount, setPaymentAmount] = useState(''); const [paymentNote, setPaymentNote] = useState(''); const [paymentSubmitting, setPaymentSubmitting] = useState(false); // Tab 3 state const [seeds, setSeeds] = useState([]); const [seedsLoading, setSeedsLoading] = useState(false); const [seedsLoaded, setSeedsLoaded] = useState(false); const [revealedMnemonics, setRevealedMnemonics] = useState>(new Set()); const [seedsAlertOpen, setSeedsAlertOpen] = useState(false); const [exportAlertOpen, setExportAlertOpen] = useState(false); // ── Tab 1: User Wallets ──────────────────────────── const fetchUserList = useCallback(async (search?: string) => { setUserListLoading(true); try { const query = search ? `?search=${encodeURIComponent(search)}` : ''; const res = await fetch(`/api/wallets/bulk${query}`); if (!res.ok) throw new Error('Failed to fetch'); const data = await res.json(); setUserList(data); } catch { toast.error('Failed to load user list'); } finally { setUserListLoading(false); } }, []); useEffect(() => { const debounce = setTimeout(() => { fetchUserList(userSearch); }, 300); return () => clearTimeout(debounce); }, [userSearch, fetchUserList]); const selectUser = async (userId: number) => { setSelectedUserId(userId); setSelectedUser(null); setSelectedUserLoading(true); try { const res = await fetch(`/api/wallets/${userId}`); if (!res.ok) throw new Error('Failed to fetch'); const data = await res.json(); setSelectedUser(data); } catch { toast.error('Failed to load user wallets'); } finally { setSelectedUserLoading(false); } }; // ── Tab 2: Owner Summary ────────────────────────── useEffect(() => { const fetchOverview = async () => { setOverviewLoading(true); try { const res = await fetch('/api/wallets/overview'); if (!res.ok) throw new Error('Failed to fetch'); const data = await res.json(); setOverview(data); } catch { toast.error('Failed to load overview'); } finally { setOverviewLoading(false); } }; fetchOverview(); }, []); const handleRecordPayment = async () => { const amount = parseFloat(paymentAmount); if (isNaN(amount) || amount <= 0) { toast.error('Enter a valid amount'); return; } setPaymentSubmitting(true); try { const res = await fetch('/api/wallets/record-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paidAmount: amount, note: paymentNote }), }); if (!res.ok) throw new Error('Failed'); toast.success('Payment recorded successfully'); setPaymentAmount(''); setPaymentNote(''); // Refetch overview const overviewRes = await fetch('/api/wallets/overview'); if (overviewRes.ok) { const data = await overviewRes.json(); setOverview(data); } } catch { toast.error('Failed to record payment'); } finally { setPaymentSubmitting(false); } }; // ── Tab 3: Seed Phrases ──────────────────────────── const handleLoadSeeds = async () => { setSeedsLoading(true); try { const res = await fetch('/api/wallets/seeds'); if (!res.ok) throw new Error('Failed'); const data = await res.json(); setSeeds(data); setSeedsLoaded(true); toast.success(`Loaded ${data.length} seed phrases`); } catch { toast.error('Failed to load seeds'); } finally { setSeedsLoading(false); setSeedsAlertOpen(false); } }; const handleExportSeeds = () => { window.open('/api/wallets/export-seeds', '_blank'); setExportAlertOpen(false); }; const toggleMnemonic = (walletId: number) => { setRevealedMnemonics((prev) => { const next = new Set(prev); if (next.has(walletId)) { next.delete(walletId); } else { next.add(walletId); } return next; }); }; // ── Render ───────────────────────────────────────── const currencyTypes = ['BTC', 'LTC', 'ETH', 'USDT', 'USDC']; return (

Wallets

Manage crypto wallets, view balances, and track commissions

User Wallets Owner Summary {isSuperAdmin && ( Seeds )} {/* ── Tab 1: User Wallets ─────────────────────── */}
{/* Left: User List */} Users with Wallets
setUserSearch(e.target.value)} />
{userListLoading ? (
{[1, 2, 3, 4, 5].map((i) => (
))}
) : userList.length === 0 ? (

No users with wallets found

) : (
{userList.map((u) => ( ))}
)}
{/* Right: Selected User Details */}
{selectedUserLoading && ( <>
{[1, 2, 3, 4, 5, 6].map((i) => ( ))}
{[1, 2, 3].map((i) => ( ))} )} {!selectedUserLoading && !selectedUser && (

Select a user from the list to view their wallets

)} {!selectedUserLoading && selectedUser && ( <> {/* Profile Card */}
{selectedUser.username || `User#${selectedUser.id}`}
{statusBadge(selectedUser.status)}
Telegram ID: {selectedUser.telegramId}

Main Balance

${selectedUser.totalBalance.toFixed(2)}

Bonus Balance

${selectedUser.bonusBalance.toFixed(2)}

Country

{selectedUser.country || '—'}

City

{selectedUser.city || '—'}

{/* Wallets Table */} Wallets ({selectedUser.wallets.length}) {selectedUser.wallets.length === 0 ? (

No wallets found for this user

) : ( Type Address Balance Created {selectedUser.wallets.map((w) => ( {walletTypeBadge(w.walletType)} {formatBalance(w.balance)} {new Date(w.createdAt).toLocaleDateString()} ))}
)}
)}
{/* ── Tab 2: Owner Summary ────────────────────── */} {overviewLoading ? (
{[1, 2, 3, 4].map((i) => ( ))}
) : overview ? ( <> {/* KPI Cards */}

Total USD Value

${overview.totalUsd.toFixed(2)}

Total Users

{overview.totalUsers}

Active Wallets

{overview.totalWallets}

Commission Due

${overview.commissionDue.toFixed(2)}

{/* Balances by Currency */} Balances by Currency Currency Wallet Count Total Balance {currencyTypes.map((type) => ( {walletTypeBadge(type)} {overview.walletCounts[type] || 0} {formatBalance(overview.totals[type] || 0)} ))}
{/* Commission Section */} Commission Management Current rate: {(overview.commissionRate * 100).toFixed(0)}% {/* Commission Stats */}

Rate

{(overview.commissionRate * 100).toFixed(0)}%

Total Balance

${overview.totalUsd.toFixed(2)}

Commission Amount

${overview.currentCommission.toFixed(2)}

Last Paid

${overview.lastPaidAmount.toFixed(2)}

Commission Due: ${overview.commissionDue.toFixed(2)}
{/* Record Payment Form */}

Record Payment

setPaymentAmount(e.target.value)} />
setPaymentNote(e.target.value)} />
{/* Payment History */}

Payment History

{overview.payments.length === 0 ? (
No payment records yet
) : (
Date Amount (USD) Note {overview.payments.map((p) => ( {new Date(p.createdAt).toLocaleDateString()} ${p.paidAmountUsd.toFixed(2)} {p.note || '—'} ))}
)}
) : null}
{/* ── Tab 3: Seed Phrases ────────────────────── */} {isSuperAdmin && ( <> {/* Commission Warning */} {overview && overview.commissionDue > 0 && (

Outstanding Commission: ${overview.commissionDue.toFixed(2)}

Please pay the outstanding commission before accessing seed phrases.

)} {/* Action Buttons */}
⚠️ Confirm Access to Seed Phrases You are about to access sensitive cryptographic seed phrases. This action will be logged in the audit trail. Only proceed if you are authorized. Cancel Proceed {seedsLoaded && ( ⚠️ Confirm CSV Export This will download all seed phrases as a CSV file containing sensitive data. The export will be logged in the audit trail. Ensure you handle this file securely. Cancel Export CSV )}
{/* Seeds Table */} {seedsLoading && ( {[1, 2, 3, 4].map((i) => ( ))} )} {!seedsLoading && seedsLoaded && ( Seed Phrases ({seeds.length}) Sensitive data — handle with care {seeds.length === 0 ? (

No seed phrases found

) : (
User Type Address Derivation Mnemonic {seeds.map((s) => ( {s.username} {walletTypeBadge(s.walletType)} {s.derivationPath || '—'}
{revealedMnemonics.has(s.walletId) ? s.mnemonic : '••••••••••••••••'}
))}
)}
)} {!seedsLoading && !seedsLoaded && (

Click "Unlock & Load Seed Phrases" to view sensitive data

)} )}
); }