Global movie year setting #7
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026 - Checklist</title>
|
||||
<title>Pool Oscars - Checklist</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
<style>
|
||||
@@ -79,7 +79,7 @@
|
||||
<div class="container mt-5">
|
||||
<h2>Checklist</h2>
|
||||
<div id="watched-ratio" class="ratio"></div>
|
||||
<h3 class="fs-6" style="margin-bottom: -1px;">Temps avant les Oscars (15 mars 2026) - <span id="time-left"></span></h3>
|
||||
<h3 class="fs-6" style="margin-bottom: -1px;">Temps avant les Oscars (15 mars <span id="oscar-year">—</span>) - <span id="time-left"></span></h3>
|
||||
<h3 class="fs-6"><span id="movies-per-day"></span></h3>
|
||||
<div class="progress my-3">
|
||||
<div id="progress-bar" class="progress-bar" role="progressbar" style="width: 0%; background-color: red;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
|
||||
+19
-1
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026 - Admin</title>
|
||||
<title>Pool Oscars - Admin</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
<style>
|
||||
@@ -52,6 +52,24 @@
|
||||
</nav>
|
||||
|
||||
<div class="container mt-5" style="max-width: 720px;">
|
||||
<h2>Admin - Année active</h2>
|
||||
<p class="text-muted mb-3">
|
||||
Cette année pilote tout le site (films affichés, checklist et statistiques).
|
||||
</p>
|
||||
|
||||
<div class="card p-3 mb-4">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-md-6">
|
||||
<label for="active_oscar_year" class="form-label">Année active</label>
|
||||
<input type="number" class="form-control" id="active_oscar_year" placeholder="2026" min="1900" max="3000">
|
||||
<div class="form-text">Ex: mets 2025 → tout le monde voit les stats/films 2025.</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button type="button" id="save-active-year" class="btn btn-warning w-100">Appliquer l’année</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Admin - Ajouter un film</h2>
|
||||
<p class="text-muted mb-4">
|
||||
Ajoute un film via l’API (<code>/api/movies/add</code>). Le serveur récupère automatiquement le titre/plot/affiche depuis OMDb avec l’<code>imdb_id</code>.
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026</title>
|
||||
<title>Pool Oscars</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
<style>
|
||||
@@ -46,7 +46,7 @@
|
||||
<!-- Footer Section -->
|
||||
<footer>
|
||||
|
||||
<p id="footer-copy"><a id="copy" href="https://github.com/quoije/oscars-pool">quoije</a> - deux mille vingt-six</p>
|
||||
<p id="footer-copy"><a id="copy" href="https://github.com/quoije/oscars-pool">quoije</a></p>
|
||||
<p id="footer-text"></p>
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
window.onload = async function () {
|
||||
const token = localStorage.getItem('auth_token');
|
||||
|
||||
const targetDate = new Date('March 15, 2026');
|
||||
async function fetchActiveYear() {
|
||||
try {
|
||||
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||
if (!res.ok) throw new Error('Failed to fetch active year');
|
||||
const data = await res.json();
|
||||
const year = Number(data?.year);
|
||||
return Number.isInteger(year) ? year : new Date().getFullYear();
|
||||
} catch (_) {
|
||||
return new Date().getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
const activeYear = await fetchActiveYear();
|
||||
document.title = `Pool Oscars ${activeYear} - Checklist`;
|
||||
const oscarYearEl = document.getElementById('oscar-year');
|
||||
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
||||
|
||||
const targetDate = new Date(`March 15, ${activeYear}`);
|
||||
const currentDate = new Date();
|
||||
const timeDifference = targetDate - currentDate;
|
||||
const daysLeft = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
|
||||
@@ -29,7 +46,7 @@ window.onload = async function () {
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
const res = await fetch('/api/movies', {
|
||||
const res = await fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
@@ -55,9 +72,13 @@ window.onload = async function () {
|
||||
watchedMovies = await watchedRes.json();
|
||||
}
|
||||
|
||||
const movieImdbIds = new Set(movies.map((m) => m.imdb_id).filter(Boolean));
|
||||
let watchedMoviesInYear = watchedMovies.filter((wm) => movieImdbIds.has(wm.imdb_id));
|
||||
|
||||
const totalMoviesCount = movies.length;
|
||||
const watchedMoviesCount = watchedMovies.length;
|
||||
document.getElementById('watched-ratio').innerText = `Vu: ${watchedMoviesCount} / ${totalMoviesCount} (${((watchedMoviesCount / totalMoviesCount) * 100).toFixed(1)}%)`;
|
||||
const watchedMoviesCount = watchedMoviesInYear.length;
|
||||
const ratioPct = totalMoviesCount > 0 ? ((watchedMoviesCount / totalMoviesCount) * 100).toFixed(1) : '0.0';
|
||||
document.getElementById('watched-ratio').innerText = `Vu: ${watchedMoviesCount} / ${totalMoviesCount} (${ratioPct}%)`;
|
||||
|
||||
function launchConfetti() {
|
||||
const duration = 3 * 1000; // 3 seconds
|
||||
@@ -111,7 +132,7 @@ window.onload = async function () {
|
||||
|
||||
const movieTableBody = document.getElementById('movie-table-body');
|
||||
movies.forEach(movie => {
|
||||
const watchedMovie = watchedMovies.find(wm => wm.imdb_id === movie.imdb_id);
|
||||
const watchedMovie = watchedMoviesInYear.find(wm => wm.imdb_id === movie.imdb_id);
|
||||
const watchedDate = watchedMovie ? new Date(watchedMovie.watchedDate).toLocaleString() : '';
|
||||
const movieRow = document.createElement('tr');
|
||||
movieRow.innerHTML = `
|
||||
@@ -169,8 +190,10 @@ window.onload = async function () {
|
||||
|
||||
if (updatedWatchedMoviesRes.ok) {
|
||||
const updatedWatchedMovies = await updatedWatchedMoviesRes.json();
|
||||
const updatedWatchedCount = updatedWatchedMovies.length;
|
||||
document.getElementById('watched-ratio').innerText = `Vu: ${updatedWatchedCount} / ${totalMoviesCount} (${((updatedWatchedCount / totalMoviesCount) * 100).toFixed(1)}%)`;
|
||||
watchedMoviesInYear = updatedWatchedMovies.filter((wm) => movieImdbIds.has(wm.imdb_id));
|
||||
const updatedWatchedCount = watchedMoviesInYear.length;
|
||||
const updatedPct = totalMoviesCount > 0 ? ((updatedWatchedCount / totalMoviesCount) * 100).toFixed(1) : '0.0';
|
||||
document.getElementById('watched-ratio').innerText = `Vu: ${updatedWatchedCount} / ${totalMoviesCount} (${updatedPct}%)`;
|
||||
updateProgressBar(updatedWatchedCount, totalMoviesCount);
|
||||
|
||||
// Recalculate and update movies per day
|
||||
|
||||
@@ -33,6 +33,9 @@ window.onload = async function () {
|
||||
const form = document.getElementById('add-movie-form');
|
||||
const yearInput = document.getElementById('oscar_year');
|
||||
|
||||
const activeYearInput = document.getElementById('active_oscar_year');
|
||||
const saveActiveYearBtn = document.getElementById('save-active-year');
|
||||
|
||||
const manageYearSelect = document.getElementById('manage_year');
|
||||
const adminMoviesBody = document.getElementById('admin-movies-body');
|
||||
const refreshMoviesBtn = document.getElementById('refresh-movies');
|
||||
@@ -57,6 +60,7 @@ window.onload = async function () {
|
||||
const editDescriptionEl = document.getElementById('edit_description');
|
||||
|
||||
let moviesById = new Map();
|
||||
let activeYear = null;
|
||||
|
||||
function showResponse(kind, message) {
|
||||
responseEl.classList.remove('d-none', 'alert-success', 'alert-danger', 'alert-warning');
|
||||
@@ -71,6 +75,40 @@ window.onload = async function () {
|
||||
return n;
|
||||
}
|
||||
|
||||
async function fetchActiveYear() {
|
||||
try {
|
||||
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||
if (!res.ok) throw new Error('Failed to fetch active year');
|
||||
const data = await res.json();
|
||||
const year = Number(data?.year);
|
||||
return Number.isInteger(year) ? year : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function setActiveYear(newYear) {
|
||||
const res = await fetch('/api/settings/year', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ year: newYear })
|
||||
});
|
||||
if (!res.ok) {
|
||||
let msg = `Erreur (${res.status})`;
|
||||
try {
|
||||
const data = await res.json();
|
||||
msg = data.message || data.error || msg;
|
||||
} catch (_) {}
|
||||
throw new Error(msg);
|
||||
}
|
||||
const data = await res.json();
|
||||
const year = Number(data?.year);
|
||||
return Number.isInteger(year) ? year : newYear;
|
||||
}
|
||||
|
||||
function isValidImdbId(value) {
|
||||
return /^tt\d{5,}$/.test(String(value || '').trim());
|
||||
}
|
||||
@@ -157,9 +195,19 @@ window.onload = async function () {
|
||||
const stillExists = years.map(String).includes(previousSelection);
|
||||
manageYearSelect.value = stillExists ? previousSelection : '';
|
||||
|
||||
// Default add-year to latest year if empty
|
||||
if (years.length > 0 && !yearInput.value) {
|
||||
yearInput.value = String(years[0]);
|
||||
// Prefer defaulting UI to active year (if selection is empty/invalid).
|
||||
const hasSelection = !!manageYearSelect.value;
|
||||
if (!hasSelection && activeYear && years.map(String).includes(String(activeYear))) {
|
||||
manageYearSelect.value = String(activeYear);
|
||||
}
|
||||
|
||||
// Default add-year to active year (or latest year) if empty
|
||||
if (!yearInput.value) {
|
||||
if (activeYear) {
|
||||
yearInput.value = String(activeYear);
|
||||
} else if (years.length > 0) {
|
||||
yearInput.value = String(years[0]);
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore
|
||||
@@ -409,6 +457,39 @@ window.onload = async function () {
|
||||
}
|
||||
|
||||
// Initial load
|
||||
activeYear = await fetchActiveYear();
|
||||
if (activeYearInput && activeYear) {
|
||||
activeYearInput.value = String(activeYear);
|
||||
document.title = `Pool Oscars ${activeYear} - Admin`;
|
||||
}
|
||||
|
||||
if (saveActiveYearBtn && activeYearInput) {
|
||||
saveActiveYearBtn.addEventListener('click', async () => {
|
||||
const y = parseYear(activeYearInput.value);
|
||||
if (!y) {
|
||||
showResponse('warning', 'Année invalide. Exemple attendu: 2026');
|
||||
return;
|
||||
}
|
||||
saveActiveYearBtn.disabled = true;
|
||||
const oldText = saveActiveYearBtn.textContent;
|
||||
saveActiveYearBtn.textContent = 'Application...';
|
||||
try {
|
||||
activeYear = await setActiveYear(y);
|
||||
activeYearInput.value = String(activeYear);
|
||||
yearInput.value = String(activeYear);
|
||||
document.title = `Pool Oscars ${activeYear} - Admin`;
|
||||
showResponse('success', `Année active appliquée: ${activeYear}`);
|
||||
await refreshYears();
|
||||
await loadMoviesForManagement();
|
||||
} catch (err) {
|
||||
showResponse('danger', err.message || 'Erreur réseau');
|
||||
} finally {
|
||||
saveActiveYearBtn.disabled = false;
|
||||
saveActiveYearBtn.textContent = oldText;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await refreshYears();
|
||||
await loadMoviesForManagement();
|
||||
};
|
||||
|
||||
+28
-7
@@ -1,5 +1,22 @@
|
||||
window.onload = async function () {
|
||||
const targetDate = new Date('March 15, 2026');
|
||||
async function fetchActiveYear() {
|
||||
try {
|
||||
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||
if (!res.ok) throw new Error('Failed to fetch active year');
|
||||
const data = await res.json();
|
||||
const year = Number(data?.year);
|
||||
return Number.isInteger(year) ? year : new Date().getFullYear();
|
||||
} catch (_) {
|
||||
return new Date().getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
const activeYear = await fetchActiveYear();
|
||||
document.title = `Pool Oscars ${activeYear} - Films`;
|
||||
const oscarYearEl = document.getElementById('oscar-year');
|
||||
if (oscarYearEl) oscarYearEl.textContent = String(activeYear);
|
||||
|
||||
const targetDate = new Date(`March 15, ${activeYear}`);
|
||||
const currentDate = new Date();
|
||||
const timeDifference = targetDate - currentDate;
|
||||
const daysLeft = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
|
||||
@@ -29,7 +46,7 @@ window.onload = async function () {
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
const res = await fetch('/api/movies', {
|
||||
const res = await fetch(`/api/movies?year=${encodeURIComponent(String(activeYear))}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': `Bearer ${token}` },
|
||||
});
|
||||
@@ -56,14 +73,18 @@ window.onload = async function () {
|
||||
}
|
||||
|
||||
const moviesList = document.getElementById('movies-list');
|
||||
const movieImdbIds = new Set(movies.map((m) => m.imdb_id).filter(Boolean));
|
||||
const watchedMoviesInYear = watchedMovies.filter((wm) => movieImdbIds.has(wm.imdb_id));
|
||||
|
||||
const totalMoviesCount = movies.length;
|
||||
const watchedMoviesCount = watchedMovies.length;
|
||||
const ratioText = `Vu: ${watchedMoviesCount} / ${totalMoviesCount} (${((watchedMoviesCount / totalMoviesCount) * 100).toFixed(1)}%)`;
|
||||
const watchedMoviesCount = watchedMoviesInYear.length;
|
||||
const ratioPct = totalMoviesCount > 0 ? ((watchedMoviesCount / totalMoviesCount) * 100).toFixed(1) : '0.0';
|
||||
const ratioText = `Vu: ${watchedMoviesCount} / ${totalMoviesCount} (${ratioPct}%)`;
|
||||
document.getElementById('watched-ratio').innerText = ratioText;
|
||||
|
||||
movies.forEach(movie => {
|
||||
const isChecked = watchedMovies.some(watchedMovie => watchedMovie.imdb_id === movie.imdb_id);
|
||||
const watchedMovie = watchedMovies.find(wm => wm.imdb_id === movie.imdb_id);
|
||||
const isChecked = watchedMoviesInYear.some(watchedMovie => watchedMovie.imdb_id === movie.imdb_id);
|
||||
const watchedMovie = watchedMoviesInYear.find(wm => wm.imdb_id === movie.imdb_id);
|
||||
const watchedDate = watchedMovie ? new Date(watchedMovie.watchedDate).toLocaleString() : '';
|
||||
|
||||
const movieDiv = document.createElement('div');
|
||||
@@ -108,7 +129,7 @@ window.onload = async function () {
|
||||
|
||||
movieCards.forEach(card => {
|
||||
const imdbId = card.getAttribute('data-imdb-id');
|
||||
const isWatched = watchedMovies.some(watchedMovie => watchedMovie.imdb_id === imdbId);
|
||||
const isWatched = watchedMoviesInYear.some(watchedMovie => watchedMovie.imdb_id === imdbId);
|
||||
|
||||
if (showUnwatchedOnly && isWatched) {
|
||||
card.style.display = 'none';
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
window.onload = async function () {
|
||||
const token = localStorage.getItem('auth_token');
|
||||
|
||||
async function fetchActiveYear() {
|
||||
try {
|
||||
const res = await fetch('/api/settings/year', { method: 'GET' });
|
||||
if (!res.ok) throw new Error('Failed to fetch active year');
|
||||
const data = await res.json();
|
||||
const year = Number(data?.year);
|
||||
return Number.isInteger(year) ? year : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const decoded = JSON.parse(atob(token.split('.')[1])); // Manually decoding JWT
|
||||
@@ -28,7 +40,15 @@ window.onload = async function () {
|
||||
}
|
||||
|
||||
try {
|
||||
const statsRes = await fetch('/api/users/stats', {
|
||||
const activeYear = await fetchActiveYear();
|
||||
if (activeYear) {
|
||||
document.title = `Pool Oscars ${activeYear} - Statistiques des utilisateurs`;
|
||||
const h2 = document.querySelector('h2');
|
||||
if (h2) h2.textContent = `Statistiques des utilisateurs (${activeYear})`;
|
||||
}
|
||||
|
||||
const statsUrl = activeYear ? `/api/users/stats?year=${encodeURIComponent(String(activeYear))}` : '/api/users/stats';
|
||||
const statsRes = await fetch(statsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026 - Films</title>
|
||||
<title>Pool Oscars - Films</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
<style>
|
||||
@@ -106,7 +106,7 @@
|
||||
<!-- This will display the ratio -->
|
||||
</div>
|
||||
|
||||
<h3 class="fs-6">Temps avant les Oscars (15 mars 2026) - <span id="time-left"></span></h3>
|
||||
<h3 class="fs-6">Temps avant les Oscars (15 mars <span id="oscar-year">—</span>) - <span id="time-left"></span></h3>
|
||||
<h3 class="fs-6 mb-0">
|
||||
|
||||
</h3>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026 - Enregistrement</title>
|
||||
<title>Pool Oscars - Enregistrement</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pool Oscars 2026 - Statistiques des utilisateurs</title>
|
||||
<title>Pool Oscars - Statistiques des utilisateurs</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.navbar-custom {
|
||||
|
||||
+28
-3
@@ -3,6 +3,7 @@ const bcrypt = require("bcryptjs");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const User = require("../models/User");
|
||||
const Movie = require("../models/Movie");
|
||||
const Setting = require("../models/Setting");
|
||||
const axios = require("axios");
|
||||
|
||||
const router = express.Router();
|
||||
@@ -11,6 +12,8 @@ const router = express.Router();
|
||||
|
||||
const woof = process.env.DOG_NAMES ? process.env.DOG_NAMES.split(",") : [];
|
||||
|
||||
const ACTIVE_YEAR_KEY = "active_oscar_year";
|
||||
|
||||
// Fetch movie details from OMDb API
|
||||
async function fetchMovieDetailsFromOmdb(imdb_id) {
|
||||
const apiKey = process.env.OMDB_API; // Replace with your actual OMDb API key
|
||||
@@ -57,16 +60,38 @@ function parseOscarYear(raw) {
|
||||
return n;
|
||||
}
|
||||
|
||||
async function getOrInitActiveYear() {
|
||||
const existing = await Setting.findOne({ key: ACTIVE_YEAR_KEY });
|
||||
if (existing && typeof existing.value === "number") return existing.value;
|
||||
|
||||
// Initialize from latest movie year if available, else from current year.
|
||||
const latestMovie = await Movie.findOne({ year: { $type: "number" } })
|
||||
.sort({ year: -1 })
|
||||
.select("year");
|
||||
|
||||
const fallbackYear = latestMovie?.year || new Date().getFullYear();
|
||||
const year = parseOscarYear(fallbackYear) || new Date().getFullYear();
|
||||
|
||||
await Setting.findOneAndUpdate(
|
||||
{ key: ACTIVE_YEAR_KEY },
|
||||
{ $set: { value: year } },
|
||||
{ upsert: true, new: true }
|
||||
);
|
||||
|
||||
return year;
|
||||
}
|
||||
|
||||
// Get user stats with watched movie titles and details from the movies collection
|
||||
router.get("/stats", verifyToken, async (req, res) => {
|
||||
try {
|
||||
// Fetch all users
|
||||
const users = await User.find();
|
||||
|
||||
const year = parseOscarYear(req.query.year);
|
||||
// If a year is explicitly provided, use it; otherwise default to the global active year.
|
||||
const year = parseOscarYear(req.query.year) || await getOrInitActiveYear();
|
||||
|
||||
// Fetch movies (optionally filtered by year)
|
||||
const allMovies = await Movie.find(year ? { year } : {});
|
||||
const allMovies = await Movie.find({ year });
|
||||
const totalMoviesCount = allMovies.length;
|
||||
|
||||
if (totalMoviesCount === 0) {
|
||||
@@ -92,7 +117,7 @@ router.get("/stats", verifyToken, async (req, res) => {
|
||||
|
||||
return {
|
||||
name: user.name,
|
||||
year: year || null,
|
||||
year,
|
||||
watchedCount,
|
||||
totalMoviesCount,
|
||||
watchedRatio: `${watchedRatio}%`,
|
||||
|
||||
Reference in New Issue
Block a user