saq1b's picture
Upload 4 files
b61cc42 verified
raw
history blame
11.1 kB
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 = `
<tr>
<td colspan="5" class="text-center py-4 text-red-500">
Error loading data. Please try again later.
</td>
</tr>
`;
}
}
// 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 = `
<tr>
<td colspan="5" class="text-center py-4">No matching results found</td>
</tr>
`;
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 = `
<td class="py-3 px-6 text-left">${item.Name}</td>
<td class="py-3 px-6 text-left">${formatType(item.Type)}</td>
<td class="py-3 px-6 text-right">${item.TimesTraded.toLocaleString()}</td>
<td class="py-3 px-6 text-right">${item.UniqueCirculation.toLocaleString()}</td>
<td class="py-3 px-6 text-right">
<span class="px-2 py-1 rounded ${demandLevel.color} text-white">
${item.DemandMultiple.toFixed(2)}
</span>
</td>
`;
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();
});