Delete script.js
Browse files
script.js
DELETED
@@ -1,302 +0,0 @@
|
|
1 |
-
document.addEventListener('DOMContentLoaded', function() {
|
2 |
-
// API URL
|
3 |
-
const apiUrl = 'https://badimo.nyc3.digitaloceanspaces.com/trade/frequency/snapshot/month/latest.json';
|
4 |
-
|
5 |
-
// Global variables
|
6 |
-
let tradingData = [];
|
7 |
-
let currentSortColumn = 'DemandMultiple';
|
8 |
-
let currentSortDirection = 'desc';
|
9 |
-
let demandChart = null;
|
10 |
-
let typeChart = null;
|
11 |
-
|
12 |
-
// Fetch data from API
|
13 |
-
async function fetchData() {
|
14 |
-
try {
|
15 |
-
const response = await fetch(apiUrl);
|
16 |
-
if (!response.ok) {
|
17 |
-
throw new Error('Network response was not ok');
|
18 |
-
}
|
19 |
-
tradingData = await response.json();
|
20 |
-
|
21 |
-
// Update last updated date
|
22 |
-
const now = new Date();
|
23 |
-
document.getElementById('lastUpdated').textContent = `Data as of ${now.toLocaleDateString()} ${now.toLocaleTimeString()}`;
|
24 |
-
|
25 |
-
// Process data
|
26 |
-
processData();
|
27 |
-
|
28 |
-
// Update UI
|
29 |
-
updateStatistics();
|
30 |
-
populateTypeFilter();
|
31 |
-
renderTable();
|
32 |
-
createCharts();
|
33 |
-
|
34 |
-
} catch (error) {
|
35 |
-
console.error('Failed to fetch data:', error);
|
36 |
-
document.getElementById('tableBody').innerHTML = `
|
37 |
-
<tr>
|
38 |
-
<td colspan="5" class="text-center py-4 text-red-500">
|
39 |
-
Error loading data. Please try again later.
|
40 |
-
</td>
|
41 |
-
</tr>
|
42 |
-
`;
|
43 |
-
}
|
44 |
-
}
|
45 |
-
|
46 |
-
// Process data for additional insights
|
47 |
-
function processData() {
|
48 |
-
// You can add any data preprocessing here if needed
|
49 |
-
// For now we'll just sort the data by the default column
|
50 |
-
sortData(currentSortColumn, currentSortDirection);
|
51 |
-
}
|
52 |
-
|
53 |
-
// Update statistics cards
|
54 |
-
function updateStatistics() {
|
55 |
-
const totalItems = tradingData.length;
|
56 |
-
const totalTrades = tradingData.reduce((sum, item) => sum + item.TimesTraded, 0);
|
57 |
-
const avgDemandMultiple = (tradingData.reduce((sum, item) => sum + item.DemandMultiple, 0) / totalItems).toFixed(2);
|
58 |
-
const totalCirculation = tradingData.reduce((sum, item) => sum + item.UniqueCirculation, 0);
|
59 |
-
|
60 |
-
document.getElementById('totalItems').textContent = totalItems.toLocaleString();
|
61 |
-
document.getElementById('totalTrades').textContent = totalTrades.toLocaleString();
|
62 |
-
document.getElementById('avgDemandMultiple').textContent = avgDemandMultiple;
|
63 |
-
document.getElementById('totalCirculation').textContent = totalCirculation.toLocaleString();
|
64 |
-
}
|
65 |
-
|
66 |
-
// Populate type filter dropdown
|
67 |
-
function populateTypeFilter() {
|
68 |
-
const types = [...new Set(tradingData.map(item => item.Type))];
|
69 |
-
const select = document.getElementById('typeFilter');
|
70 |
-
|
71 |
-
types.forEach(type => {
|
72 |
-
const option = document.createElement('option');
|
73 |
-
option.value = type;
|
74 |
-
option.textContent = type;
|
75 |
-
select.appendChild(option);
|
76 |
-
});
|
77 |
-
}
|
78 |
-
|
79 |
-
// Sort data based on column and direction
|
80 |
-
function sortData(column, direction) {
|
81 |
-
tradingData.sort((a, b) => {
|
82 |
-
let comparison = 0;
|
83 |
-
|
84 |
-
if (typeof a[column] === 'string') {
|
85 |
-
comparison = a[column].localeCompare(b[column]);
|
86 |
-
} else {
|
87 |
-
comparison = a[column] - b[column];
|
88 |
-
}
|
89 |
-
|
90 |
-
return direction === 'asc' ? comparison : -comparison;
|
91 |
-
});
|
92 |
-
}
|
93 |
-
|
94 |
-
// Render table with current data and filters
|
95 |
-
function renderTable() {
|
96 |
-
const tableBody = document.getElementById('tableBody');
|
97 |
-
const searchInput = document.getElementById('searchInput').value.toLowerCase();
|
98 |
-
const typeFilter = document.getElementById('typeFilter').value;
|
99 |
-
|
100 |
-
// Filter data
|
101 |
-
const filteredData = tradingData.filter(item => {
|
102 |
-
const nameMatch = item.Name.toLowerCase().includes(searchInput);
|
103 |
-
const typeMatch = typeFilter === 'all' || item.Type === typeFilter;
|
104 |
-
return nameMatch && typeMatch;
|
105 |
-
});
|
106 |
-
|
107 |
-
// Clear table
|
108 |
-
tableBody.innerHTML = '';
|
109 |
-
|
110 |
-
// Add rows
|
111 |
-
if (filteredData.length === 0) {
|
112 |
-
tableBody.innerHTML = `
|
113 |
-
<tr>
|
114 |
-
<td colspan="5" class="text-center py-4">No matching results found</td>
|
115 |
-
</tr>
|
116 |
-
`;
|
117 |
-
return;
|
118 |
-
}
|
119 |
-
|
120 |
-
filteredData.forEach(item => {
|
121 |
-
const row = document.createElement('tr');
|
122 |
-
row.className = 'border-b border-gray-200 hover:bg-gray-50';
|
123 |
-
|
124 |
-
// Calculate demand level for color coding
|
125 |
-
const demandLevel = getDemandLevel(item.DemandMultiple);
|
126 |
-
|
127 |
-
row.innerHTML = `
|
128 |
-
<td class="py-3 px-6 text-left">${item.Name}</td>
|
129 |
-
<td class="py-3 px-6 text-left">${formatType(item.Type)}</td>
|
130 |
-
<td class="py-3 px-6 text-right">${item.TimesTraded.toLocaleString()}</td>
|
131 |
-
<td class="py-3 px-6 text-right">${item.UniqueCirculation.toLocaleString()}</td>
|
132 |
-
<td class="py-3 px-6 text-right">
|
133 |
-
<span class="px-2 py-1 rounded ${demandLevel.color} text-white">
|
134 |
-
${item.DemandMultiple.toFixed(2)}
|
135 |
-
</span>
|
136 |
-
</td>
|
137 |
-
`;
|
138 |
-
|
139 |
-
tableBody.appendChild(row);
|
140 |
-
});
|
141 |
-
}
|
142 |
-
|
143 |
-
// Format type for better display
|
144 |
-
function formatType(type) {
|
145 |
-
if (type.includes('.')) {
|
146 |
-
return type.split('.').pop();
|
147 |
-
}
|
148 |
-
return type;
|
149 |
-
}
|
150 |
-
|
151 |
-
// Get demand level for color coding
|
152 |
-
function getDemandLevel(demand) {
|
153 |
-
if (demand >= 5) return { level: 'Very High', color: 'bg-red-600' };
|
154 |
-
if (demand >= 3) return { level: 'High', color: 'bg-orange-500' };
|
155 |
-
if (demand >= 2) return { level: 'Medium', color: 'bg-yellow-500' };
|
156 |
-
if (demand >= 1) return { level: 'Low', color: 'bg-green-500' };
|
157 |
-
return { level: 'Very Low', color: 'bg-blue-500' };
|
158 |
-
}
|
159 |
-
|
160 |
-
// Create data visualizations
|
161 |
-
function createCharts() {
|
162 |
-
createDemandChart();
|
163 |
-
createTypeDistributionChart();
|
164 |
-
}
|
165 |
-
|
166 |
-
// Create demand multiple chart
|
167 |
-
function createDemandChart() {
|
168 |
-
const top10ByDemand = [...tradingData]
|
169 |
-
.sort((a, b) => b.DemandMultiple - a.DemandMultiple)
|
170 |
-
.slice(0, 10);
|
171 |
-
|
172 |
-
const ctx = document.getElementById('demandChart').getContext('2d');
|
173 |
-
|
174 |
-
if (demandChart) {
|
175 |
-
demandChart.destroy();
|
176 |
-
}
|
177 |
-
|
178 |
-
demandChart = new Chart(ctx, {
|
179 |
-
type: 'bar',
|
180 |
-
data: {
|
181 |
-
labels: top10ByDemand.map(item => item.Name),
|
182 |
-
datasets: [{
|
183 |
-
label: 'Demand Multiple',
|
184 |
-
data: top10ByDemand.map(item => item.DemandMultiple),
|
185 |
-
backgroundColor: 'rgba(99, 102, 241, 0.7)',
|
186 |
-
borderColor: 'rgb(79, 70, 229)',
|
187 |
-
borderWidth: 1
|
188 |
-
}]
|
189 |
-
},
|
190 |
-
options: {
|
191 |
-
responsive: true,
|
192 |
-
maintainAspectRatio: false,
|
193 |
-
scales: {
|
194 |
-
y: {
|
195 |
-
beginAtZero: true,
|
196 |
-
title: {
|
197 |
-
display: true,
|
198 |
-
text: 'Demand Multiple'
|
199 |
-
}
|
200 |
-
}
|
201 |
-
}
|
202 |
-
}
|
203 |
-
});
|
204 |
-
}
|
205 |
-
|
206 |
-
// Create type distribution chart
|
207 |
-
function createTypeDistributionChart() {
|
208 |
-
const typeDistribution = {};
|
209 |
-
tradingData.forEach(item => {
|
210 |
-
const type = formatType(item.Type);
|
211 |
-
if (!typeDistribution[type]) {
|
212 |
-
typeDistribution[type] = 0;
|
213 |
-
}
|
214 |
-
typeDistribution[type]++;
|
215 |
-
});
|
216 |
-
|
217 |
-
const ctx = document.getElementById('typeChart').getContext('2d');
|
218 |
-
|
219 |
-
if (typeChart) {
|
220 |
-
typeChart.destroy();
|
221 |
-
}
|
222 |
-
|
223 |
-
const colors = [
|
224 |
-
'rgba(99, 102, 241, 0.7)',
|
225 |
-
'rgba(239, 68, 68, 0.7)',
|
226 |
-
'rgba(16, 185, 129, 0.7)',
|
227 |
-
'rgba(245, 158, 11, 0.7)',
|
228 |
-
'rgba(139, 92, 246, 0.7)',
|
229 |
-
'rgba(14, 165, 233, 0.7)'
|
230 |
-
];
|
231 |
-
|
232 |
-
typeChart = new Chart(ctx, {
|
233 |
-
type: 'pie',
|
234 |
-
data: {
|
235 |
-
labels: Object.keys(typeDistribution),
|
236 |
-
datasets: [{
|
237 |
-
label: 'Item Count',
|
238 |
-
data: Object.values(typeDistribution),
|
239 |
-
backgroundColor: colors,
|
240 |
-
borderWidth: 1
|
241 |
-
}]
|
242 |
-
},
|
243 |
-
options: {
|
244 |
-
responsive: true,
|
245 |
-
maintainAspectRatio: false,
|
246 |
-
plugins: {
|
247 |
-
legend: {
|
248 |
-
position: 'right',
|
249 |
-
}
|
250 |
-
}
|
251 |
-
}
|
252 |
-
});
|
253 |
-
}
|
254 |
-
|
255 |
-
// Set up event listeners
|
256 |
-
function setupEventListeners() {
|
257 |
-
// Sort headers
|
258 |
-
document.querySelectorAll('th[data-sort]').forEach(header => {
|
259 |
-
header.addEventListener('click', () => {
|
260 |
-
const column = header.dataset.sort;
|
261 |
-
|
262 |
-
// Toggle sort direction or set to asc if it's a new column
|
263 |
-
const direction =
|
264 |
-
column === currentSortColumn && currentSortDirection === 'asc' ? 'desc' : 'asc';
|
265 |
-
|
266 |
-
// Update sort variables
|
267 |
-
currentSortColumn = column;
|
268 |
-
currentSortDirection = direction;
|
269 |
-
|
270 |
-
// Update UI
|
271 |
-
updateSortIcons();
|
272 |
-
sortData(column, direction);
|
273 |
-
renderTable();
|
274 |
-
});
|
275 |
-
});
|
276 |
-
|
277 |
-
// Search input
|
278 |
-
document.getElementById('searchInput').addEventListener('input', renderTable);
|
279 |
-
|
280 |
-
// Type filter
|
281 |
-
document.getElementById('typeFilter').addEventListener('change', renderTable);
|
282 |
-
}
|
283 |
-
|
284 |
-
// Update sort icons
|
285 |
-
function updateSortIcons() {
|
286 |
-
document.querySelectorAll('th[data-sort]').forEach(header => {
|
287 |
-
const icon = header.querySelector('.sort-icon');
|
288 |
-
if (header.dataset.sort === currentSortColumn) {
|
289 |
-
icon.textContent = currentSortDirection === 'asc' ? ' ↑' : ' ↓';
|
290 |
-
header.classList.add('font-bold');
|
291 |
-
} else {
|
292 |
-
icon.textContent = '';
|
293 |
-
header.classList.remove('font-bold');
|
294 |
-
}
|
295 |
-
});
|
296 |
-
}
|
297 |
-
|
298 |
-
// Initialize
|
299 |
-
fetchData();
|
300 |
-
setupEventListeners();
|
301 |
-
updateSortIcons();
|
302 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|