AI-Powered Whales Activity Tracker
Filters & Settings
Loading data...
Recent Whale Transactions
| Time | Asset | Type | Quantity | Value ($) | Whale ID | AI Signal |
|---|---|---|---|---|---|---|
| No activity to display. | ||||||
Top Assets Targeted by Whales
| Rank | Asset | Type | Total Whale Volume ($) | Transactions | AI Trend Forecast |
|---|---|---|---|---|---|
| No top assets to display. | |||||
AI-Powered Market Insights
No AI insights available at the moment.
No AI insights available for the current selection.
`; return; } insights.forEach(insight => { const insightCard = document.createElement('div'); insightCard.className = 'bg-blue-50 border border-blue-200 p-4 rounded-lg shadow'; insightCard.innerHTML = `${insight.text}
Relevance: ${insight.relevance}
`; aiInsightsContainer.appendChild(insightCard); }); }; // --- Data Filtering and Application Logic --- const applyAllFiltersAndRender = () => { showLoading(true); // Simulate API call delay setTimeout(() => { const currentAssetType = assetTypeFilter ? assetTypeFilter.value : 'all'; const currentSymbolSearch = assetSymbolSearch ? assetSymbolSearch.value.toUpperCase().trim() : ''; const currentTimeframe = timeframeFilter ? timeframeFilter.value : 'all_time'; let filteredTransactions = mockWhaleTransactions.filter(txn => { const typeMatch = currentAssetType === 'all' || txn.assetType === currentAssetType; const symbolMatch = currentSymbolSearch === '' || txn.assetSymbol.toUpperCase().includes(currentSymbolSearch); let dateMatch = true; if (currentTimeframe !== 'all_time') { const now = new Date(); let startDate = new Date(); if (currentTimeframe === '24h') startDate.setDate(now.getDate() - 1); else if (currentTimeframe === '7d') startDate.setDate(now.getDate() - 7); else if (currentTimeframe === '30d') startDate.setDate(now.getDate() - 30); dateMatch = txn.timestamp >= startDate; } return typeMatch && symbolMatch && dateMatch; }); // For Top Assets, re-calculate based on filtered transactions or use pre-gen if filters are broad // For simplicity, we'll filter the pre-generated top assets by type if selected. let filteredTopAssets = mockTopAssets.filter(asset => { const typeMatch = currentAssetType === 'all' || asset.assetType === currentAssetType; const symbolMatch = currentSymbolSearch === '' || asset.assetSymbol.toUpperCase().includes(currentSymbolSearch); return typeMatch && symbolMatch; }); // Re-rank filtered top assets filteredTopAssets.forEach((asset, index) => asset.rank = index + 1); // AI Insights could be filtered too, but for now, show all let filteredAiInsights = mockAiInsights; renderLiveActivity(filteredTransactions); renderTopAssets(filteredTopAssets); renderAiInsights(filteredAiInsights); showLoading(false); showMessage('Data refreshed successfully!', 'success'); }, 1000); // Simulate 1 second loading }; // --- Tab Management --- const TABS_ORDER = ['liveActivityTab', 'topAssetsTab', 'aiInsightsTab']; const updateTabButtonsState = () => { if (!prevTabButton || !nextTabButton) return; const currentIndex = TABS_ORDER.indexOf(currentActiveTab); prevTabButton.disabled = currentIndex === 0; nextTabButton.disabled = currentIndex === TABS_ORDER.length - 1; prevTabButton.classList.toggle('opacity-50', prevTabButton.disabled); prevTabButton.classList.toggle('cursor-not-allowed', prevTabButton.disabled); nextTabButton.classList.toggle('opacity-50', nextTabButton.disabled); nextTabButton.classList.toggle('cursor-not-allowed', nextTabButton.disabled); }; const switchTab = (tabId) => { if (!tabId) return; currentActiveTab = tabId; tabContents.forEach(content => { if (content) content.style.display = content.id === tabId ? 'block' : 'none'; }); tabButtons.forEach(button => { if (button) button.classList.toggle('active', button.dataset.tab === tabId); }); updateTabButtonsState(); }; if (tabButtons.length > 0) { tabButtons.forEach(button => { button.addEventListener('click', () => { const tabId = button.dataset.tab; switchTab(tabId); }); }); } if (nextTabButton) { nextTabButton.addEventListener('click', () => { const currentIndex = TABS_ORDER.indexOf(currentActiveTab); if (currentIndex < TABS_ORDER.length - 1) { switchTab(TABS_ORDER[currentIndex + 1]); } }); } if (prevTabButton) { prevTabButton.addEventListener('click', () => { const currentIndex = TABS_ORDER.indexOf(currentActiveTab); if (currentIndex > 0) { switchTab(TABS_ORDER[currentIndex - 1]); } }); } // --- PDF Download Functionality --- const downloadPDF = () => { // Check if the main jsPDF library (window.jspdf.jsPDF) is loaded if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { showMessage('PDF generation library (jsPDF) is not loaded. Please try again later.', 'error'); console.error("jsPDF main library (window.jspdf.jsPDF) is not loaded."); return; } const { jsPDF: JSPDF } = window.jspdf; // Correctly get the constructor const doc = new JSPDF(); // Check if the autoTable plugin is loaded by verifying its presence on the instance if (typeof doc.autoTable !== 'function') { showMessage('PDF generation plugin (autoTable) is not loaded. Please ensure it is included after jsPDF.', 'error'); console.error("jsPDF autoTable plugin (doc.autoTable) is not available. Check script order and loading."); return; } const today = new Date().toLocaleDateString('en-US'); const appTitle = "AI-Powered Whales Activity Tracker"; doc.setFontSize(18); doc.text(appTitle, 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Report Generated: ${today}`, 14, 30); let tableData, tableHeaders, title; if (currentActiveTab === 'liveActivityTab') { title = "Recent Whale Transactions"; tableHeaders = [["Time", "Asset", "Type", "Quantity", "Value ($)", "Whale ID", "AI Signal"]]; const activityBody = document.getElementById('liveActivityTableBody'); if (!activityBody) { showMessage('Cannot find activity data for PDF.', 'error'); return; } tableData = Array.from(activityBody.rows).map(row => Array.from(row.cells).map(cell => cell.innerText) ); if (activityBody.rows.length === 1 && activityBody.rows[0].cells.length === 1 && activityBody.rows[0].cells[0].colSpan === 7) { // "No activity" message tableData = []; // Empty data for PDF if table shows "No activity" } } else if (currentActiveTab === 'topAssetsTab') { title = "Top Assets Targeted by Whales"; tableHeaders = [["Rank", "Asset", "Type", "Total Volume ($)", "Transactions", "AI Trend"]]; const assetsBody = document.getElementById('topAssetsTableBody'); if (!assetsBody) { showMessage('Cannot find asset data for PDF.', 'error'); return; } tableData = Array.from(assetsBody.rows).map(row => Array.from(row.cells).map(cell => cell.innerText) ); if (assetsBody.rows.length === 1 && assetsBody.rows[0].cells.length === 1 && assetsBody.rows[0].cells[0].colSpan === 6) { tableData = []; } } else if (currentActiveTab === 'aiInsightsTab') { title = "AI-Powered Market Insights"; doc.setFontSize(14); doc.text(title, 14, 40); let yPos = 50; const insightsDiv = document.getElementById('aiInsightsContainer'); if (!insightsDiv) { showMessage('Cannot find insights data for PDF.', 'error'); return; } const insightElements = insightsDiv.children; if (insightElements.length === 1 && insightElements[0].tagName === 'P' && insightElements[0].innerText.includes("No AI insights")) { doc.text("No AI insights available for the current selection.", 14, yPos); } else { Array.from(insightElements).forEach(insightCard => { if (yPos > 270) { // Check for page break doc.addPage(); yPos = 20; } const texts = Array.from(insightCard.querySelectorAll('p')).map(p => p.innerText); texts.forEach(text => { const splitText = doc.splitTextToSize(text, 180); // 180 is width doc.text(splitText, 14, yPos); yPos += (splitText.length * 5); // Adjust spacing based on lines }); yPos += 5; // Extra space between insights }); } doc.save(`${appTitle} - ${title} - ${today}.pdf`); showMessage('PDF downloaded successfully!', 'success'); return; // Insights PDF is different, so return here } else { showMessage('No active tab selected for PDF export.', 'error'); return; } if (tableData && tableData.length > 0) { doc.autoTable({ head: tableHeaders, body: tableData, startY: 40, theme: 'grid', headStyles: { fillColor: [59, 130, 246] }, // Tailwind blue-500 styles: { font: 'Inter', fontSize: 8 }, // Match font if possible didDrawPage: function (data) { // Add footer to each page if needed // doc.setFontSize(10); // doc.text('Page ' + doc.internal.getNumberOfPages(), data.settings.margin.left, doc.internal.pageSize.height - 10); } }); } else { doc.setFontSize(12); doc.text(`No data available to export for "${title}".`, 14, 45); } doc.save(`${appTitle} - ${title} - ${today}.pdf`); showMessage('PDF downloaded successfully!', 'success'); }; // --- Event Listeners --- if (applyFiltersButton) { applyFiltersButton.addEventListener('click', applyAllFiltersAndRender); } if (downloadPdfButton) { downloadPdfButton.addEventListener('click', downloadPDF); } // --- Initialization --- const initializeApp = () => { if (currentYearSpan) { currentYearSpan.textContent = new Date().getFullYear(); } generateMockData(); // Generate initial data applyAllFiltersAndRender(); // Render with default filters switchTab(TABS_ORDER[0]); // Set initial active tab updateTabButtonsState(); // Set initial state of nav buttons }; initializeApp(); });