diff --git a/public/checklist.html b/public/checklist.html index 2233d28..2f93a9d 100644 --- a/public/checklist.html +++ b/public/checklist.html @@ -72,6 +72,7 @@ Films Checklist Statistiques + FAQ Admin diff --git a/public/control.html b/public/control.html index e3f71db..b256dba 100644 --- a/public/control.html +++ b/public/control.html @@ -43,6 +43,7 @@ Films Checklist Statistiques + FAQ Admin
diff --git a/public/faq.html b/public/faq.html new file mode 100644 index 0000000..77d990e --- /dev/null +++ b/public/faq.html @@ -0,0 +1,178 @@ + + + + + + Pool Oscars - FAQ + + + + + + + + + +
+
+ Chargement… + +
+
+ + + + +
+
+

FAQ

+
Rapide, simple, sans drama.
+
+ +
+
+
+
En 10 secondes
+
+ Pool Oscars te sert à suivre les films de l’année, les regarder dans le lecteur, + cocher ce que tu as vu, et voir les stats (toi + les autres). +
+
+ +
+
+

+ +

+
+
+ C’est une app “pool” pour suivre les films nommés aux Oscars (par année). Tu vois la liste, tu regardes, + tu coches, et tu compares ta progression avec les autres. +
+
+
+ +
+

+ +

+
+
+ Tu browses les films. Clique sur l’affiche ou le titre pour ouvrir le lecteur. L’icône IMDb ouvre la fiche + du film dans un nouvel onglet. +
+
+
+ +
+

+ +

+
+
+ C’est la version “tableau simple” pour cocher vite ce que tu as vu. Ça calcule ton ratio, une barre de + progression et une estimation “à voir par jour” avant la date des Oscars. +
+
+
+ +
+

+ +

+
+
+ Un classement (films vus / points) + des onglets “Gagnants” et “100%”. Tu peux aussi cliquer sur un nom pour + voir les films que la personne a regardés. +
+
+
+ +
+

+ +

+
+
+ Ça arrive quand ton compte a un mot de passe temporaire. Tu dois en définir un nouveau une fois, puis c’est + réglé. +
+
+
+ +
+

+ +

+
+
+ Seulement pour les admins: réglages (année active, date des Oscars, lecteur), gestion des films et des + utilisateurs. +
+
+
+
+
+
+
+ + + + + + diff --git a/public/js/app_faq.js b/public/js/app_faq.js new file mode 100644 index 0000000..2d13822 --- /dev/null +++ b/public/js/app_faq.js @@ -0,0 +1,184 @@ +function base64UrlToJson(b64url) { + const s = String(b64url || '') + .replace(/-/g, '+') + .replace(/_/g, '/'); + const padded = s + '='.repeat((4 - (s.length % 4)) % 4); + return JSON.parse(atob(padded)); +} + +function decodeJwtPayload(token) { + const parts = String(token || '').split('.'); + if (parts.length < 2) return null; + try { return base64UrlToJson(parts[1]); } catch (_) { return null; } +} + +function isExpired(decoded) { + const exp = Number(decoded?.exp); + if (!Number.isFinite(exp) || exp <= 0) return false; + const now = Math.floor(Date.now() / 1000); + return exp < now; +} + +function createPageLoader(options = {}) { + const title = String(options.title || 'Chargement…'); + let progress = 0; + let removed = false; + let navResizeObserver = null; + + // Reuse a preloaded overlay if present (prevents initial "clear" flash). + const preloadedOverlay = document.getElementById('page-loader-overlay'); + const overlay = preloadedOverlay || document.createElement('div'); + if (!preloadedOverlay) { + overlay.className = 'page-loader-overlay'; + overlay.setAttribute('role', 'status'); + overlay.setAttribute('aria-live', 'polite'); + overlay.setAttribute('aria-busy', 'true'); + + overlay.innerHTML = ` +
+ ${title} + +
+ `; + } else { + const label = overlay.querySelector('.visually-hidden'); + if (label) label.textContent = title; + } + + const barEl = () => overlay.querySelector('#page-loader-bar'); + + function clampPct(p) { + const n = Number(p); + if (!Number.isFinite(n)) return 0; + return Math.max(0, Math.min(100, n)); + } + + function setProgress(p) { + progress = clampPct(p); + const bar = barEl(); + if (bar) bar.style.width = `${progress}%`; + } + + function getNavHeightPx() { + const nav = document.querySelector('nav.navbar'); + if (!nav) return 0; + const rect = nav.getBoundingClientRect(); + const h = Number(rect?.height) || 0; + return h > 0 ? Math.round(h) : 0; + } + + function updateOverlayTopOffset() { + overlay.style.setProperty('--page-loader-top', `${getNavHeightPx()}px`); + } + + function ensureMounted() { + if (removed) return; + updateOverlayTopOffset(); + document.body.classList.add('page-loading'); + if (!overlay.isConnected) document.body.appendChild(overlay); + + if (!navResizeObserver) { + const nav = document.querySelector('nav.navbar'); + if (nav && typeof ResizeObserver !== 'undefined') { + try { + navResizeObserver = new ResizeObserver(() => updateOverlayTopOffset()); + navResizeObserver.observe(nav); + } catch (_) { + navResizeObserver = null; + } + } + } + } + + function hideAndRemoveSoon() { + if (removed) return; + overlay.classList.add('page-loader-hide'); + overlay.setAttribute('aria-busy', 'false'); + document.body.classList.remove('page-loading'); + window.setTimeout(() => { + removed = true; + if (navResizeObserver) { + try { navResizeObserver.disconnect(); } catch (_) {} + navResizeObserver = null; + } + try { overlay.remove(); } catch (_) {} + }, 220); + } + + function done() { + setProgress(100); + hideAndRemoveSoon(); + } + + function fail() { + setProgress(Math.max(progress, 95)); + window.setTimeout(() => hideAndRemoveSoon(), 200); + } + + ensureMounted(); + setProgress(8); + + return { setProgress, done, fail }; +} + +async function fetchActiveYear() { + try { + const res = await fetch('/api/settings/year', { method: 'GET', cache: 'no-cache' }); + 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; + } +} + +window.addEventListener('DOMContentLoaded', async function () { + const pageLoader = createPageLoader({ title: 'Chargement de la FAQ' }); + pageLoader.setProgress(14); + + const token = localStorage.getItem('auth_token'); + if (!token) { + window.location.href = '/'; + return; + } + + const decoded = decodeJwtPayload(token); + if (!decoded || isExpired(decoded)) { + try { localStorage.removeItem('auth_token'); } catch (_) {} + window.location.href = '/'; + return; + } + + if (decoded.mustChangePassword) { + window.location.href = '/change-password.html'; + return; + } + + // Header is usually set by app_header_boot, but keep it consistent if it ran before DOM was ready. + const nameEl = document.getElementById('user-name'); + if (nameEl && !nameEl.textContent) nameEl.textContent = decoded.name || ''; + + if (decoded.admin) { + const adminLink = document.getElementById('admin-control-link'); + if (adminLink) adminLink.classList.remove('d-none'); + } + + pageLoader.setProgress(40); + const activeYear = await fetchActiveYear(); + if (activeYear) document.title = `Pool Oscars (${activeYear}) - FAQ`; + pageLoader.setProgress(90); + + const logoffBtn = document.getElementById('log-off'); + if (logoffBtn) { + logoffBtn.addEventListener('click', function () { + localStorage.removeItem('auth_token'); + window.location.href = '/'; + }); + } + + pageLoader.done(); +}); + diff --git a/public/movies.html b/public/movies.html index b0ce59d..c4af61b 100644 --- a/public/movies.html +++ b/public/movies.html @@ -59,6 +59,7 @@ Films Checklist Statistiques + FAQ Admin
diff --git a/public/player.html b/public/player.html index c4feba4..e12ea44 100644 --- a/public/player.html +++ b/public/player.html @@ -127,6 +127,7 @@ Films Checklist Statistiques + FAQ Admin
diff --git a/public/user-stat.html b/public/user-stat.html index fb28ff6..55cad56 100644 --- a/public/user-stat.html +++ b/public/user-stat.html @@ -65,6 +65,7 @@ Films Checklist Statistiques + FAQ Admin