188 lines
7.4 KiB
JavaScript
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);
|
|
}
|
|
})();
|