You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
4.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>📊 Video Storage Analytics</title>
<style>
body {
font-family: Arial, sans-serif;
background: #111;
color: #eee;
text-align: center;
}
table {
margin: auto;
border-collapse: collapse;
width: 90%;
margin-top: 20px;
}
th,
td {
border: 1px solid #444;
padding: 10px;
cursor: pointer;
}
th {
background: #333;
}
tr:nth-child(even) {
background: #222;
}
th.sort-asc::after {
content: " ▲";
}
th.sort-desc::after {
content: " ▼";
}
#search {
margin: 10px;
padding: 8px;
width: 300px;
}
.pagination {
margin-top: 15px;
}
.pagination button {
background: #222;
color: #eee;
border: 1px solid #444;
margin: 0 5px;
padding: 6px 12px;
cursor: pointer;
}
.pagination button.active {
background: #555;
}
</style>
</head>
<body>
<h1>📊 Video Storage Analytics</h1>
<button onclick="window.location.href='/refresh'">🔄 Refresh Data</button>
<input type="text" id="search" placeholder="Search users...">
<table id="analytics-table">
<thead>
<tr>
<th>User</th>
<th>Platform</th>
<th>Total Storage (GB)</th>
<th>Video Count</th>
<th>Avg Size per Video (GB)</th>
</tr>
</thead>
<tbody>
{% for key, stats in storage_usage %}
{% set user, platform = key.split("::") %}
<tr>
<td><a href="/user/{{ user }}" style="color: #4af;">{{ user }}</a></td>
<td>{{ platform }}</td>
<td>{{ "%.2f"|format(stats.total_size) }}</td>
<td>{{ stats.video_count }}</td>
<td>{{ "%.2f"|format(avg_sizes[key]) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pagination" id="pagination"></div>
<script>
const table = document.getElementById('analytics-table');
const headers = table.querySelectorAll('th');
const searchInput = document.getElementById('search');
const rows = Array.from(table.querySelector('tbody').rows);
const pagination = document.getElementById('pagination');
let sortDirection = {};
let currentPage = 1;
const rowsPerPage = 100;
// Sorting
headers.forEach((header, index) => {
header.addEventListener('click', () => {
const isNumeric = index >= 2;
const dir = sortDirection[index] === 'asc' ? 'desc' : 'asc';
sortDirection = { [index]: dir };
headers.forEach(h => h.classList.remove('sort-asc', 'sort-desc'));
header.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc');
rows.sort((a, b) => {
const aVal = isNumeric ? parseFloat(a.cells[index].innerText) : a.cells[index].innerText.toLowerCase();
const bVal = isNumeric ? parseFloat(b.cells[index].innerText) : b.cells[index].innerText.toLowerCase();
return dir === 'asc' ? (aVal > bVal ? 1 : -1) : (aVal < bVal ? 1 : -1);
});
updateTable();
});
});
// Search
searchInput.addEventListener('keyup', () => {
currentPage = 1;
updateTable();
});
// Pagination
function updatePagination(totalPages) {
pagination.innerHTML = '';
for (let i = 1; i <= totalPages; i++) {
const btn = document.createElement('button');
btn.innerText = i;
btn.classList.toggle('active', i === currentPage);
btn.addEventListener('click', () => {
currentPage = i;
updateTable();
});
pagination.appendChild(btn);
}
}
function updateTable() {
const term = searchInput.value.toLowerCase();
const filtered = rows.filter(row => row.cells[0].innerText.toLowerCase().includes(term));
const totalPages = Math.ceil(filtered.length / rowsPerPage);
updatePagination(totalPages);
table.querySelector('tbody').innerHTML = '';
const start = (currentPage - 1) * rowsPerPage;
const paginated = filtered.slice(start, start + rowsPerPage);
paginated.forEach(row => table.querySelector('tbody').appendChild(row));
}
updateTable(); // Initial render
</script>
</body>
</html>