Spaces:
Running
Running
| // API Base URL | |
| const API_BASE_URL = 'https://api.solbotsentinel.com/v1'; | |
| // Initialize tooltips and fetch initial data | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Add tooltips to action buttons | |
| const actionButtons = document.querySelectorAll('[data-action]'); | |
| actionButtons.forEach(button => { | |
| const action = button.getAttribute('data-action'); | |
| button.setAttribute('data-tooltip', action.charAt(0).toUpperCase() + action.slice(1)); | |
| }); | |
| // Load initial data | |
| fetchWallets(); | |
| fetchPerformanceStats(); | |
| // Set up event listeners | |
| setupEventListeners(); | |
| }); | |
| function setupEventListeners() { | |
| // Add wallet button | |
| document.querySelector('button[data-action="add-wallet"]')?.addEventListener('click', addWallet); | |
| // Copy trade buttons | |
| document.querySelectorAll('button[data-action="copy-trade"]').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const wallet = btn.closest('tr').querySelector('td:first-child p').textContent; | |
| startCopyTrading(wallet); | |
| }); | |
| }); | |
| // Stop trade buttons | |
| document.querySelectorAll('button[data-action="stop-trade"]').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const wallet = btn.closest('tr').querySelector('td:first-child p').textContent; | |
| stopCopyTrading(wallet); | |
| }); | |
| }); | |
| } | |
| async function fetchWallets() { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/wallets`); | |
| const data = await response.json(); | |
| updateWalletTable(data); | |
| } catch (error) { | |
| console.error('Failed to fetch wallets:', error); | |
| showNotification('error', 'Failed to load wallet data'); | |
| } | |
| } | |
| async function fetchPerformanceStats() { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/performance`); | |
| const data = await response.json(); | |
| updatePerformanceStats(data); | |
| } catch (error) { | |
| console.error('Failed to fetch performance stats:', error); | |
| showNotification('error', 'Failed to load performance data'); | |
| } | |
| } | |
| function updateWalletTable(wallets) { | |
| const tbody = document.querySelector('tbody'); | |
| tbody.innerHTML = ''; | |
| wallets.forEach(wallet => { | |
| const row = document.createElement('tr'); | |
| row.className = 'hover:bg-gray-700 transition'; | |
| row.innerHTML = ` | |
| <td class="py-4"> | |
| <div class="flex items-center gap-3"> | |
| <div class="w-8 h-8 rounded-full bg-primary flex items-center justify-center"> | |
| <i data-feather="user" class="w-4 h-4"></i> | |
| </div> | |
| <div> | |
| <p class="font-medium">${wallet.address.substring(0, 4)}...${wallet.address.substring(wallet.address.length - 4)}</p> | |
| <p class="text-xs text-gray-400">Added ${formatDate(wallet.addedAt)}</p> | |
| </div> | |
| </div> | |
| </td> | |
| <td> | |
| <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${wallet.status === 'copying' ? 'bg-green-900 text-green-300' : 'bg-gray-700 text-gray-300'}"> | |
| <i data-feather="${wallet.status === 'copying' ? 'check-circle' : 'pause'}" class="w-3 h-3 mr-1"></i> | |
| ${wallet.status === 'copying' ? 'Copying' : 'Watching'} | |
| </span> | |
| </td> | |
| <td class="${wallet.pnl >= 0 ? 'text-secondary' : 'text-red-500'} font-bold">${wallet.pnl >= 0 ? '+' : ''}${wallet.pnl.toFixed(2)}</td> | |
| <td> | |
| <div class="flex gap-2"> | |
| <button class="p-2 rounded-lg hover:bg-gray-700 transition" data-action="view"> | |
| <i data-feather="eye"></i> | |
| </button> | |
| ${wallet.status === 'copying' ? | |
| `<button class="p-2 rounded-lg hover:bg-gray-700 transition" data-action="stop-trade"> | |
| <i data-feather="stop-circle" class="text-red-500"></i> | |
| </button>` : | |
| `<button class="p-2 rounded-lg hover:bg-gray-700 transition" data-action="copy-trade"> | |
| <i data-feather="play" class="text-secondary"></i> | |
| </button>` | |
| } | |
| </div> | |
| </td> | |
| `; | |
| tbody.appendChild(row); | |
| }); | |
| feather.replace(); | |
| } | |
| function updatePerformanceStats(stats) { | |
| document.querySelector('.stats-total-profit').textContent = `+${stats.totalProfit.toFixed(2)}`; | |
| document.querySelector('.stats-total-loss').textContent = `-${Math.abs(stats.totalLoss).toFixed(2)}`; | |
| document.querySelector('.stats-win-rate').textContent = `${stats.winRate.toFixed(1)}%`; | |
| document.querySelector('.stats-active-trades').textContent = stats.activeTrades; | |
| } | |
| async function addWallet() { | |
| const walletInput = document.querySelector('input[placeholder="Enter wallet address"]'); | |
| const walletAddress = walletInput.value.trim(); | |
| if (!walletAddress || walletAddress.length < 10) { | |
| showNotification('error', 'Please enter a valid wallet address'); | |
| return; | |
| } | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/wallets`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ address: walletAddress }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(await response.text()); | |
| } | |
| walletInput.value = ''; | |
| showNotification('success', `Wallet ${walletAddress.substring(0, 6)}... added successfully`); | |
| fetchWallets(); // Refresh wallet list | |
| } catch (error) { | |
| console.error('Failed to add wallet:', error); | |
| showNotification('error', error.message || 'Failed to add wallet'); | |
| } | |
| } | |
| async function startCopyTrading(walletAddress) { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/copy-trade/start`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ address: walletAddress }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(await response.text()); | |
| } | |
| showNotification('success', `Started copy trading for wallet ${walletAddress.substring(0, 6)}...`); | |
| fetchWallets(); // Refresh wallet list | |
| fetchPerformanceStats(); // Refresh stats | |
| } catch (error) { | |
| console.error('Failed to start copy trading:', error); | |
| showNotification('error', error.message || 'Failed to start copy trading'); | |
| } | |
| } | |
| async function stopCopyTrading(walletAddress) { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/copy-trade/stop`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ address: walletAddress }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(await response.text()); | |
| } | |
| showNotification('success', `Stopped copy trading for wallet ${walletAddress.substring(0, 6)}...`); | |
| fetchWallets(); // Refresh wallet list | |
| fetchPerformanceStats(); // Refresh stats | |
| } catch (error) { | |
| console.error('Failed to stop copy trading:', error); | |
| showNotification('error', error.message || 'Failed to stop copy trading'); | |
| } | |
| } | |
| function showNotification(type, message) { | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 transform transition-all duration-300 ${type === 'error' ? 'bg-red-500' : 'bg-green-500'}`; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.style.transform = 'translateX(200%)'; | |
| setTimeout(() => notification.remove(), 300); | |
| }, 3000); | |
| } | |
| function formatDate(timestamp) { | |
| const now = new Date(); | |
| const date = new Date(timestamp); | |
| const diffInDays = Math.floor((now - date) / (1000 * 60 * 60 * 24)); | |
| if (diffInDays === 0) return 'today'; | |
| if (diffInDays === 1) return 'yesterday'; | |
| if (diffInDays < 7) return `${diffInDays} days ago`; | |
| if (diffInDays < 30) return `${Math.floor(diffInDays/7)} weeks ago`; | |
| return `${Math.floor(diffInDays/30)} months ago`; | |
| } | |