Files
oscars-pool/public/js/app_header_boot.js
T
Mathieu Moran 70b309d1ae Add visibility toggle for picks features and rename Mes choix to Mes picks
- Renamed 'Mes choix' to 'Mes picks' across all pages and JavaScript files
- Added visibility toggle in control panel (Réglages > Visibilité) to control:
  - Display of 'Mes picks' button in navigation
  - Display of 'Bon picks' column in user statistics
- Combined both visibility options into single toggle for simplicity
- Added backend API endpoints for visibility configuration
- Implemented localStorage caching for instant visibility updates
- Fixed flash of content issues when navigating between pages
- Updated stats endpoint to calculate correct picks on-the-fly from current winners
- Renamed 'Bonnes réponses' to 'Bon picks' in user statistics table
2025-12-27 22:03:01 -05:00

109 lines
3.3 KiB
JavaScript

// Runs early (defer) to populate the navbar before page-specific JS.
// Goal: avoid "late" header updates that look like flicker.
(function () {
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;
}
const token = localStorage.getItem('auth_token');
if (!token) return;
const decoded = decodeJwtPayload(token);
if (!decoded) return;
// Fast-path redirect checks (match page scripts behavior).
if (decoded.mustChangePassword && !/\/change-password\.html(\?|#|$)/.test(window.location.pathname)) {
window.location.href = '/change-password.html';
return;
}
if (isExpired(decoded)) {
try { localStorage.removeItem('auth_token'); } catch (_) {}
if (!/\/index\.html$/.test(window.location.pathname) && window.location.pathname !== '/') {
window.location.href = '/';
}
return;
}
const nameEl = document.getElementById('user-name');
if (nameEl && !nameEl.textContent) {
nameEl.textContent = decoded.name || '';
}
const adminLink = document.getElementById('admin-control-link');
if (adminLink && decoded.admin) {
adminLink.classList.remove('d-none');
}
// Check visibility config for picks button
const picksButton = document.getElementById('nav-link-picks');
if (picksButton) {
// Check cached value first for immediate display
const cachedConfig = localStorage.getItem('visibility_config_cache');
let cachedValue = null;
let cachedTimestamp = null;
if (cachedConfig) {
try {
const parsed = JSON.parse(cachedConfig);
cachedValue = parsed.value;
cachedTimestamp = parsed.timestamp;
} catch (e) {
// Invalid cache, ignore
}
}
// Use cached value if it's less than 5 minutes old
const cacheAge = cachedTimestamp ? Date.now() - cachedTimestamp : Infinity;
const useCache = cachedValue !== null && cacheAge < 5 * 60 * 1000;
if (useCache) {
// Show button immediately based on cached value
if (cachedValue.showPicksButton !== false) {
picksButton.style.display = '';
}
} else {
// Default to showing while we fetch
picksButton.style.display = '';
}
// Fetch fresh value from server
fetch('/api/settings/visibility-config', { cache: 'no-store' })
.then(res => res.json())
.then(config => {
// Cache the result
localStorage.setItem('visibility_config_cache', JSON.stringify({
value: config,
timestamp: Date.now()
}));
// Update display based on fresh value
if (config.showPicksButton !== false) {
picksButton.style.display = '';
} else {
picksButton.style.display = 'none';
}
})
.catch(() => {
// On error, keep current state (already set from cache or default)
});
}
})();