Files
ShooterHub/frontend/js/nav.js
2026-04-02 11:24:30 +02:00

188 lines
7.4 KiB
JavaScript

// ── Navbar injection + auth guard ─────────────────────────────────────────────
(function () {
const access = getAccess();
const isAuth = !!access;
const path = window.location.pathname;
const PROTECTED = ['/dashboard.html', '/gears.html', '/reloads.html',
'/profile.html', '/sessions.html', '/admin.html',
'/messages.html', '/friends.html'];
const ADMIN_ONLY = ['/admin.html'];
const AUTH_ONLY = ['/login.html', '/register.html'];
if (PROTECTED.includes(path) && !isAuth) {
window.location.href = '/login.html';
return;
}
if (AUTH_ONLY.includes(path) && isAuth) {
window.location.href = '/dashboard.html';
return;
}
const LANGS = [
{ code: 'en', label: 'EN', name: 'English' },
{ code: 'fr', label: 'FR', name: 'Français' },
{ code: 'de', label: 'DE', name: 'Deutsch' },
{ code: 'es', label: 'ES', name: 'Español' },
];
const currentLang = getLang();
const langSelector = `
<li class="nav-item dropdown ms-2">
<a class="nav-link dropdown-toggle px-2" href="#" data-bs-toggle="dropdown"
id="langMenu" title="Language">
<span id="currentLangLabel">${currentLang.toUpperCase()}</span>
</a>
<ul class="dropdown-menu dropdown-menu-end">
${LANGS.map(l => `
<li>
<a class="dropdown-item ${l.code === currentLang ? 'active' : ''}"
href="#" data-lang="${l.code}">
${l.label}${l.name}
</a>
</li>`).join('')}
</ul>
</li>`;
// Built after profile load (need is_staff)
function buildNav(isStaff) {
const adminLink = isStaff
? `<li class="nav-item"><a class="nav-link text-warning" href="/admin.html"><i class="bi bi-shield-lock me-1"></i>${t('nav.admin')}</a></li>`
: '';
const mainLinks = isAuth ? `
${adminLink}
<li class="nav-item"><a class="nav-link" href="/dashboard.html">${t('nav.dashboard')}</a></li>
<li class="nav-item"><a class="nav-link" href="/sessions.html">${t('nav.sessions')}</a></li>
<li class="nav-item"><a class="nav-link" href="/gears.html">${t('nav.gear')}</a></li>
<li class="nav-item"><a class="nav-link" href="/reloads.html">${t('nav.reloads')}</a></li>
<li class="nav-item"><a class="nav-link" href="/photos.html">${t('nav.photos')}</a></li>
<li class="nav-item"><a class="nav-link" href="/tools.html">${t('nav.tools')}</a></li>
` : `
<li class="nav-item"><a class="nav-link" href="/tools.html">${t('nav.tools')}</a></li>
`;
const authNav = isAuth ? `
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center gap-2"
href="#" data-bs-toggle="dropdown">
<span class="avatar-circle" id="navInitials">?</span>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/profile.html"><i class="bi bi-person-circle me-2"></i>${t('nav.profile')}</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item d-flex align-items-center justify-content-between" href="/messages.html">
<span><i class="bi bi-envelope me-2"></i>Messages</span>
<span class="badge bg-danger rounded-pill d-none" id="navMsgBadge"></span>
</a>
</li>
<li>
<a class="dropdown-item d-flex align-items-center justify-content-between" href="/friends.html">
<span><i class="bi bi-people me-2"></i>Friends</span>
<span class="badge bg-danger rounded-pill d-none" id="navFriendsBadge"></span>
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" id="logoutBtn" href="#"><i class="bi bi-box-arrow-right me-2"></i>${t('nav.signout')}</a></li>
</ul>
</li>
` : `
<li class="nav-item"><a class="nav-link" href="/login.html">${t('nav.signin')}</a></li>
<li class="nav-item ms-2"><a class="btn btn-primary btn-sm" href="/register.html">${t('nav.register')}</a></li>
`;
document.getElementById('navbar').innerHTML = `
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
<div class="container-fluid">
<a class="navbar-brand fw-bold" href="/index.html">
<i class="bi bi-crosshair2 me-2"></i>ShooterHub
</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#mainNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav me-auto">${mainLinks}</ul>
<ul class="navbar-nav align-items-center">
${authNav}
${langSelector}
</ul>
</div>
</div>
</nav>`;
// Apply data-i18n translations to page elements
applyTranslations();
// Load notification badges for authenticated users
if (isAuth) {
apiFetch('/social/messages/unread-count/').then(r => r.json()).then(d => {
const b = document.getElementById('navMsgBadge');
if (b && d.unread > 0) { b.textContent = d.unread; b.classList.remove('d-none'); }
}).catch(() => {});
apiFetch('/social/friends/requests/').then(r => r.json()).then(d => {
const b = document.getElementById('navFriendsBadge');
const count = Array.isArray(d) ? d.length : 0;
if (b && count > 0) { b.textContent = count; b.classList.remove('d-none'); }
}).catch(() => {});
}
// Highlight active link
document.querySelectorAll('#navbar .nav-link[href]').forEach(a => {
if (a.getAttribute('href') === path) a.classList.add('active');
});
// Logout
document.addEventListener('click', e => {
if (e.target.closest('#logoutBtn')) {
e.preventDefault();
clearTokens();
window.location.href = '/index.html';
}
});
// Admin guard (page-level, after we know is_staff)
if (ADMIN_ONLY.includes(path) && !isStaff) {
window.location.href = '/dashboard.html';
}
}
// Language selector handler (attached after navbar renders via delegation)
document.addEventListener('click', e => {
const item = e.target.closest('[data-lang]');
if (!item) return;
e.preventDefault();
const lang = item.dataset.lang;
setLang(lang);
const label = document.getElementById('currentLangLabel');
if (label) label.textContent = lang.toUpperCase();
// Persist to profile if logged in
if (isAuth) {
apiPatch('/users/profile/', { language: lang }).catch(() => {});
}
// Refresh page so Django serves the right language
window.location.reload();
});
if (isAuth) {
apiFetch('/users/profile/')
.then(r => r.json())
.then(user => {
// Sync language from profile on first load
if (user.language && user.language !== getLang()) {
setLang(user.language);
}
buildNav(!!user.is_staff);
const initials = ((user.first_name?.[0] || '') + (user.last_name?.[0] || '') ||
user.username?.[0] || '?').toUpperCase();
const el = document.getElementById('navInitials');
if (el) el.textContent = initials;
})
.catch(() => buildNav(false));
} else {
buildNav(false);
}
})();