Refactor user stats page to use tabs and cache data
Co-authored-by: mathieumoran1 <[email protected]>
This commit is contained in:
co-authored by
mathieumoran1
parent
a7b7df8689
commit
e6f3998d88
@@ -1,5 +1,7 @@
|
|||||||
window.onload = async function () {
|
window.onload = async function () {
|
||||||
const token = localStorage.getItem('auth_token');
|
const token = localStorage.getItem('auth_token');
|
||||||
|
let winnersCache = null;
|
||||||
|
let completionsCache = null;
|
||||||
|
|
||||||
async function fetchActiveYear() {
|
async function fetchActiveYear() {
|
||||||
try {
|
try {
|
||||||
@@ -172,6 +174,11 @@ window.onload = async function () {
|
|||||||
fetchCompletions(),
|
fetchCompletions(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
winnersCache = winners;
|
||||||
|
completionsCache = completions;
|
||||||
|
renderWinners(winnersCache);
|
||||||
|
renderCompletions(completionsCache);
|
||||||
|
|
||||||
if (!statsRes.ok) {
|
if (!statsRes.ok) {
|
||||||
throw new Error('Failed to fetch user stats');
|
throw new Error('Failed to fetch user stats');
|
||||||
}
|
}
|
||||||
@@ -180,9 +187,7 @@ window.onload = async function () {
|
|||||||
const userTableBody = document.getElementById('user-table-body');
|
const userTableBody = document.getElementById('user-table-body');
|
||||||
const table = document.querySelector('.user-table');
|
const table = document.querySelector('.user-table');
|
||||||
const spinner = document.getElementById('loading-spinner');
|
const spinner = document.getElementById('loading-spinner');
|
||||||
|
const statsError = document.getElementById('stats-error');
|
||||||
renderWinners(winners);
|
|
||||||
renderCompletions(completions);
|
|
||||||
|
|
||||||
// Ensure watchedRatio is treated as a number and sort in descending order
|
// Ensure watchedRatio is treated as a number and sort in descending order
|
||||||
stats.sort((a, b) => parseFloat(b.watchedRatio) - parseFloat(a.watchedRatio));
|
stats.sort((a, b) => parseFloat(b.watchedRatio) - parseFloat(a.watchedRatio));
|
||||||
@@ -205,6 +210,7 @@ window.onload = async function () {
|
|||||||
// Hide spinner and show table after loading
|
// Hide spinner and show table after loading
|
||||||
spinner.style.display = 'none';
|
spinner.style.display = 'none';
|
||||||
table.style.display = 'table';
|
table.style.display = 'table';
|
||||||
|
if (statsError) statsError.classList.add('d-none');
|
||||||
|
|
||||||
// Add event listener for user links
|
// Add event listener for user links
|
||||||
document.querySelectorAll('.user-link').forEach(link => {
|
document.querySelectorAll('.user-link').forEach(link => {
|
||||||
@@ -233,9 +239,24 @@ window.onload = async function () {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading user statistics:', error);
|
console.error('Error loading user statistics:', error);
|
||||||
|
const spinner = document.getElementById('loading-spinner');
|
||||||
|
const table = document.querySelector('.user-table');
|
||||||
|
const statsError = document.getElementById('stats-error');
|
||||||
|
if (spinner) spinner.style.display = 'none';
|
||||||
|
if (table) table.style.display = 'none';
|
||||||
|
if (statsError) {
|
||||||
|
statsError.textContent = "Impossible de charger le classement pour le moment.";
|
||||||
|
statsError.classList.remove('d-none');
|
||||||
|
}
|
||||||
// Still attempt to render these sections even if stats fail (best effort)
|
// Still attempt to render these sections even if stats fail (best effort)
|
||||||
try { renderWinners(await fetchWinners()); } catch (_) {}
|
try {
|
||||||
try { renderCompletions(await fetchCompletions()); } catch (_) {}
|
if (winnersCache === null) winnersCache = await fetchWinners();
|
||||||
|
renderWinners(winnersCache);
|
||||||
|
} catch (_) {}
|
||||||
|
try {
|
||||||
|
if (completionsCache === null) completionsCache = await fetchCompletions();
|
||||||
|
renderCompletions(completionsCache);
|
||||||
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log-off functionality
|
// Log-off functionality
|
||||||
|
|||||||
+69
-35
@@ -30,6 +30,14 @@
|
|||||||
.navbar-toggler-icon {
|
.navbar-toggler-icon {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='white' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='white' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stats tabs */
|
||||||
|
.stats-tabs .nav-link {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.stats-tab-panel {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-light">
|
<body class="bg-light">
|
||||||
@@ -61,43 +69,69 @@
|
|||||||
|
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<h2>Statistiques des utilisateurs</h2>
|
<h2>Statistiques des utilisateurs</h2>
|
||||||
|
|
||||||
<!-- Winners section -->
|
<!-- Tabs -->
|
||||||
<div class="mt-4">
|
<ul class="nav nav-tabs stats-tabs mt-3" id="statsTabs" role="tablist">
|
||||||
<h4 class="mb-2">Gagnants</h4>
|
<li class="nav-item" role="presentation">
|
||||||
<div class="text-muted mb-2">Définis dans l’admin (optionnel).</div>
|
<button class="nav-link active" id="tab-ranking" data-bs-toggle="tab" data-bs-target="#pane-ranking" type="button" role="tab" aria-controls="pane-ranking" aria-selected="true">
|
||||||
<div id="winners-list" class="list-group mb-4">
|
Classement
|
||||||
<div class="list-group-item text-muted">Chargement…</div>
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="tab-winners" data-bs-toggle="tab" data-bs-target="#pane-winners" type="button" role="tab" aria-controls="pane-winners" aria-selected="false">
|
||||||
|
Gagnants
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="tab-completion" data-bs-toggle="tab" data-bs-target="#pane-completion" type="button" role="tab" aria-controls="pane-completion" aria-selected="false">
|
||||||
|
100%
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content stats-tab-panel" id="statsTabsContent">
|
||||||
|
<!-- Ranking tab -->
|
||||||
|
<div class="tab-pane fade show active" id="pane-ranking" role="tabpanel" aria-labelledby="tab-ranking" tabindex="0">
|
||||||
|
<div id="stats-error" class="alert alert-danger d-none" role="alert"></div>
|
||||||
|
|
||||||
|
<!-- Spinner shown while loading -->
|
||||||
|
<div id="loading-spinner" class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table for users and movies watched -->
|
||||||
|
<table class="table user-table" style="display: none;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Nom</th>
|
||||||
|
<th scope="col">Total des films regardés (pts)</th>
|
||||||
|
<th scope="col">Ratio</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="user-table-body">
|
||||||
|
<!-- User rows will be inserted here -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Winners tab -->
|
||||||
|
<div class="tab-pane fade" id="pane-winners" role="tabpanel" aria-labelledby="tab-winners" tabindex="0">
|
||||||
|
<h4 class="mb-2">Gagnants</h4>
|
||||||
|
<div class="text-muted mb-2">Définis dans l’admin (optionnel).</div>
|
||||||
|
<div id="winners-list" class="list-group mb-4">
|
||||||
|
<div class="list-group-item text-muted">Chargement…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 100% completers tab -->
|
||||||
|
<div class="tab-pane fade" id="pane-completion" role="tabpanel" aria-labelledby="tab-completion" tabindex="0">
|
||||||
|
<h4 class="mb-2">Utilisateurs ayant terminé 100% (par année)</h4>
|
||||||
|
<div class="text-muted mb-2">Completion = 100% pour l’année (tous les films de l’année vus).</div>
|
||||||
|
<div id="completers-by-year" class="mb-4">
|
||||||
|
<div class="text-muted">Chargement…</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 100% completers section -->
|
|
||||||
<div class="mt-4">
|
|
||||||
<h4 class="mb-2">Utilisateurs ayant terminé 100% (par année)</h4>
|
|
||||||
<div class="text-muted mb-2">Completion = 100% pour l’année (tous les films de l’année vus).</div>
|
|
||||||
<div id="completers-by-year" class="mb-4">
|
|
||||||
<div class="text-muted">Chargement…</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Spinner shown while loading -->
|
|
||||||
<div id="loading-spinner" class="spinner-border text-primary" role="status">
|
|
||||||
<span class="visually-hidden">Loading...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Table for users and movies watched -->
|
|
||||||
<table class="table user-table" style="display: none;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">Nom</th>
|
|
||||||
<th scope="col">Total des films regardés (pts)</th>
|
|
||||||
<th scope="col">Ratio</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="user-table-body">
|
|
||||||
<!-- User rows will be inserted here -->
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal for displaying movies watched -->
|
<!-- Modal for displaying movies watched -->
|
||||||
|
|||||||
Reference in New Issue
Block a user