document.addEventListener('DOMContentLoaded', function() { // API URL const apiUrl = 'https://badimo.nyc3.digitaloceanspaces.com/trade/frequency/snapshot/month/latest.json'; // Global variables let tradingData = []; let currentSortColumn = 'DemandMultiple'; let currentSortDirection = 'desc'; let demandChart = null; let typeChart = null; // Fetch data from API async function fetchData() { try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error('Network response was not ok'); } tradingData = await response.json(); // Update last updated date const now = new Date(); document.getElementById('lastUpdated').textContent = `Data as of ${now.toLocaleDateString()} ${now.toLocaleTimeString()}`; // Process data processData(); // Update UI updateStatistics(); populateTypeFilter(); renderTable(); createCharts(); } catch (error) { console.error('Failed to fetch data:', error); document.getElementById('tableBody').innerHTML = ` Error loading data. Please try again later. `; } } // Process data for additional insights function processData() { // You can add any data preprocessing here if needed // For now we'll just sort the data by the default column sortData(currentSortColumn, currentSortDirection); } // Update statistics cards function updateStatistics() { const totalItems = tradingData.length; const totalTrades = tradingData.reduce((sum, item) => sum + item.TimesTraded, 0); const avgDemandMultiple = (tradingData.reduce((sum, item) => sum + item.DemandMultiple, 0) / totalItems).toFixed(2); const totalCirculation = tradingData.reduce((sum, item) => sum + item.UniqueCirculation, 0); document.getElementById('totalItems').textContent = totalItems.toLocaleString(); document.getElementById('totalTrades').textContent = totalTrades.toLocaleString(); document.getElementById('avgDemandMultiple').textContent = avgDemandMultiple; document.getElementById('totalCirculation').textContent = totalCirculation.toLocaleString(); } // Populate type filter dropdown function populateTypeFilter() { const types = [...new Set(tradingData.map(item => item.Type))]; const select = document.getElementById('typeFilter'); types.forEach(type => { const option = document.createElement('option'); option.value = type; option.textContent = type; select.appendChild(option); }); } // Sort data based on column and direction function sortData(column, direction) { tradingData.sort((a, b) => { let comparison = 0; if (typeof a[column] === 'string') { comparison = a[column].localeCompare(b[column]); } else { comparison = a[column] - b[column]; } return direction === 'asc' ? comparison : -comparison; }); } // Render table with current data and filters function renderTable() { const tableBody = document.getElementById('tableBody'); const searchInput = document.getElementById('searchInput').value.toLowerCase(); const typeFilter = document.getElementById('typeFilter').value; // Filter data const filteredData = tradingData.filter(item => { const nameMatch = item.Name.toLowerCase().includes(searchInput); const typeMatch = typeFilter === 'all' || item.Type === typeFilter; return nameMatch && typeMatch; }); // Clear table tableBody.innerHTML = ''; // Add rows if (filteredData.length === 0) { tableBody.innerHTML = ` No matching results found `; return; } filteredData.forEach(item => { const row = document.createElement('tr'); row.className = 'border-b border-gray-200 hover:bg-gray-50'; // Calculate demand level for color coding const demandLevel = getDemandLevel(item.DemandMultiple); row.innerHTML = ` ${item.Name} ${formatType(item.Type)} ${item.TimesTraded.toLocaleString()} ${item.UniqueCirculation.toLocaleString()} ${item.DemandMultiple.toFixed(2)} `; tableBody.appendChild(row); }); } // Format type for better display function formatType(type) { if (type.includes('.')) { return type.split('.').pop(); } return type; } // Get demand level for color coding function getDemandLevel(demand) { if (demand >= 5) return { level: 'Very High', color: 'bg-red-600' }; if (demand >= 3) return { level: 'High', color: 'bg-orange-500' }; if (demand >= 2) return { level: 'Medium', color: 'bg-yellow-500' }; if (demand >= 1) return { level: 'Low', color: 'bg-green-500' }; return { level: 'Very Low', color: 'bg-blue-500' }; } // Create data visualizations function createCharts() { createDemandChart(); createTypeDistributionChart(); } // Create demand multiple chart function createDemandChart() { const top10ByDemand = [...tradingData] .sort((a, b) => b.DemandMultiple - a.DemandMultiple) .slice(0, 10); const ctx = document.getElementById('demandChart').getContext('2d'); if (demandChart) { demandChart.destroy(); } demandChart = new Chart(ctx, { type: 'bar', data: { labels: top10ByDemand.map(item => item.Name), datasets: [{ label: 'Demand Multiple', data: top10ByDemand.map(item => item.DemandMultiple), backgroundColor: 'rgba(99, 102, 241, 0.7)', borderColor: 'rgb(79, 70, 229)', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, title: { display: true, text: 'Demand Multiple' } } } } }); } // Create type distribution chart function createTypeDistributionChart() { const typeDistribution = {}; tradingData.forEach(item => { const type = formatType(item.Type); if (!typeDistribution[type]) { typeDistribution[type] = 0; } typeDistribution[type]++; }); const ctx = document.getElementById('typeChart').getContext('2d'); if (typeChart) { typeChart.destroy(); } const colors = [ 'rgba(99, 102, 241, 0.7)', 'rgba(239, 68, 68, 0.7)', 'rgba(16, 185, 129, 0.7)', 'rgba(245, 158, 11, 0.7)', 'rgba(139, 92, 246, 0.7)', 'rgba(14, 165, 233, 0.7)' ]; typeChart = new Chart(ctx, { type: 'pie', data: { labels: Object.keys(typeDistribution), datasets: [{ label: 'Item Count', data: Object.values(typeDistribution), backgroundColor: colors, borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right', } } } }); } // Set up event listeners function setupEventListeners() { // Sort headers document.querySelectorAll('th[data-sort]').forEach(header => { header.addEventListener('click', () => { const column = header.dataset.sort; // Toggle sort direction or set to asc if it's a new column const direction = column === currentSortColumn && currentSortDirection === 'asc' ? 'desc' : 'asc'; // Update sort variables currentSortColumn = column; currentSortDirection = direction; // Update UI updateSortIcons(); sortData(column, direction); renderTable(); }); }); // Search input document.getElementById('searchInput').addEventListener('input', renderTable); // Type filter document.getElementById('typeFilter').addEventListener('change', renderTable); } // Update sort icons function updateSortIcons() { document.querySelectorAll('th[data-sort]').forEach(header => { const icon = header.querySelector('.sort-icon'); if (header.dataset.sort === currentSortColumn) { icon.textContent = currentSortDirection === 'asc' ? ' ↑' : ' ↓'; header.classList.add('font-bold'); } else { icon.textContent = ''; header.classList.remove('font-bold'); } }); } // Initialize fetchData(); setupEventListeners(); updateSortIcons(); });