xinnn32's picture
buatkan saya web leaderboar lengkap dengan grafiknya untuk sebuah node
59beca2 verified
Raw
History Blame Contribute Delete
7.59 kB
document.addEventListener('DOMContentLoaded', function() {
// Sample data - in a real app, this would come from an API
const nodes = Array.from({length: 25}, (_, i) => ({
id: `NODE-${1000 + i}`,
name: `Node ${i + 1}`,
uptime: Math.floor(Math.random() * 20) + 80 + Math.random(), // 80-100%
performance: Math.floor(Math.random() * 20) + 80 + Math.random(), // 80-100%
score: Math.floor(Math.random() * 20) + 80 + Math.random(), // 80-100
status: Math.random() > 0.1 ? 'active' : 'inactive',
lastUpdated: new Date(Date.now() - Math.floor(Math.random() * 86400000)) // within 24 hours
})).sort((a, b) => b.score - a.score);
// Chart data
const performanceChartData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
datasets: [
{
label: 'Average Score',
data: [72, 75, 78, 82, 85, 87, 89],
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.1)',
tension: 0.4,
fill: true
},
{
label: 'Network Uptime %',
data: [92, 93, 94, 95, 96, 97, 98],
borderColor: '#10b981',
backgroundColor: 'rgba(16, 185, 129, 0.1)',
tension: 0.4,
fill: true
}
]
};
// Initialize chart
const ctx = document.getElementById('performance-chart').getContext('2d');
const performanceChart = new Chart(ctx, {
type: 'line',
data: performanceChartData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
labels: {
color: '#6b7280',
font: {
family: 'Inter'
}
}
}
},
scales: {
y: {
beginAtZero: false,
min: 60,
max: 100,
grid: {
color: 'rgba(209, 213, 219, 0.2)'
},
ticks: {
color: '#6b7280'
}
},
x: {
grid: {
display: false
},
ticks: {
color: '#6b7280'
}
}
}
}
});
// Pagination variables
let currentPage = 1;
const rowsPerPage = 10;
const totalPages = Math.ceil(nodes.length / rowsPerPage);
// DOM elements
const leaderboardBody = document.getElementById('leaderboard-body');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const showingCount = document.getElementById('showing-count');
const totalCount = document.getElementById('total-count');
const refreshBtn = document.getElementById('refresh-btn');
// Update count display
totalCount.textContent = nodes.length;
// Render table rows
function renderTableRows() {
leaderboardBody.innerHTML = '';
const startIndex = (currentPage - 1) * rowsPerPage;
const endIndex = Math.min(startIndex + rowsPerPage, nodes.length);
for (let i = startIndex; i < endIndex; i++) {
const node = nodes[i];
const row = document.createElement('tr');
row.className = `table-row-hover ${i % 2 === 0 ? 'bg-gray-50 dark:bg-gray-800' : 'bg-white dark:bg-gray-800'}`;
row.innerHTML = `
<td class="py-4 text-gray-700 dark:text-gray-300 font-medium">${i + 1}</td>
<td class="py-4">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-primary-100 dark:bg-gray-700 flex items-center justify-center">
<i data-feather="server" class="text-primary-600 dark:text-primary-500"></i>
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900 dark:text-white">${node.id}</div>
<div class="text-sm text-gray-500 dark:text-gray-400">${node.name}</div>
</div>
</div>
</td>
<td class="py-4">
<div class="flex items-center">
<div class="w-24 bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
<div class="bg-green-500 h-2.5 rounded-full" style="width: ${node.uptime}%"></div>
</div>
<span class="ml-2 text-sm text-gray-500 dark:text-gray-400">${node.uptime.toFixed(1)}%</span>
</div>
</td>
<td class="py-4">
<div class="flex items-center">
<div class="w-24 bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
<div class="bg-blue-500 h-2.5 rounded-full" style="width: ${node.performance}%"></div>
</div>
<span class="ml-2 text-sm text-gray-500 dark:text-gray-400">${node.performance.toFixed(1)}%</span>
</div>
</td>
<td class="py-4 text-right font-bold text-gray-900 dark:text-white">${node.score.toFixed(1)}</td>
`;
leaderboardBody.appendChild(row);
}
// Update showing count
showingCount.textContent = `${startIndex + 1}-${endIndex}`;
// Update button states
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage === totalPages;
// Refresh feather icons
feather.replace();
}
// Event listeners
prevBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
renderTableRows();
}
});
nextBtn.addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
renderTableRows();
}
});
refreshBtn.addEventListener('click', () => {
// Simulate refresh
refreshBtn.classList.add('animate-spin');
setTimeout(() => {
refreshBtn.classList.remove('animate-spin');
// In a real app, we would fetch new data here
}, 1000);
});
// Initial render
renderTableRows();
// Update chart colors for dark mode
const darkModeObserver = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains('dark');
performanceChart.options.scales.y.grid.color = isDark ? 'rgba(75, 85, 99, 0.2)' : 'rgba(209, 213, 219, 0.2)';
performanceChart.options.scales.y.ticks.color = isDark ? '#9ca3af' : '#6b7280';
performanceChart.options.scales.x.ticks.color = isDark ? '#9ca3af' : '#6b7280';
performanceChart.options.plugins.legend.labels.color = isDark ? '#9ca3af' : '#6b7280';
performanceChart.update();
});
darkModeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
});