- Fixed footer logo contrast (dark → white on dark background) - Fixed avatar sizing and gradient contrasts - Fixed testimonial layout with flexbox - Fixed signup form contrast and LastPass icon overlap - Added responsive company logos section - Fixed FAQ accordion CSS - All CSS improvements for WCAG compliance
208 lines
6.0 KiB
JavaScript
208 lines
6.0 KiB
JavaScript
/**
|
|
* TenderRadar Navigation Component
|
|
* Shared navbar for all app pages (dashboard, profile, alerts)
|
|
*/
|
|
|
|
class NavBar {
|
|
constructor() {
|
|
this.navElement = null;
|
|
this.isLoggedIn = isAuthenticated();
|
|
this.userInfo = getUserInfo();
|
|
}
|
|
|
|
/**
|
|
* Initialize and inject navbar into page
|
|
*/
|
|
init() {
|
|
this.createNavBar();
|
|
this.attachEventListeners();
|
|
this.highlightActivePage();
|
|
}
|
|
|
|
/**
|
|
* Create navbar HTML structure
|
|
*/
|
|
createNavBar() {
|
|
const nav = document.createElement('nav');
|
|
nav.className = 'app-navbar';
|
|
nav.innerHTML = this.getNavBarHTML();
|
|
|
|
// Insert at the top of body
|
|
document.body.insertBefore(nav, document.body.firstChild);
|
|
this.navElement = nav;
|
|
}
|
|
|
|
/**
|
|
* Get navbar HTML based on auth state
|
|
*/
|
|
getNavBarHTML() {
|
|
if (this.isLoggedIn && this.userInfo) {
|
|
return this.getAuthenticatedNavHTML();
|
|
} else {
|
|
return this.getUnauthenticatedNavHTML();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HTML for authenticated users
|
|
*/
|
|
getAuthenticatedNavHTML() {
|
|
const userEmail = this.userInfo.email || 'User';
|
|
|
|
return `
|
|
<header class="app-header">
|
|
<div class="nav-container">
|
|
<!-- Logo / Brand -->
|
|
<div class="nav-brand">
|
|
<a href="/dashboard.html" class="brand-link">
|
|
<img src="/logo.png" alt="TenderRadar" class="nav-logo">
|
|
<span class="brand-text">TenderRadar</span>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Mobile Toggle -->
|
|
<button class="mobile-menu-toggle" aria-label="Toggle menu">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</button>
|
|
|
|
<!-- Main Navigation -->
|
|
<div class="nav-menu-wrapper">
|
|
<ul class="nav-menu">
|
|
<li><a href="/dashboard.html" class="nav-link" data-page="dashboard">Dashboard</a></li>
|
|
<li><a href="/tenders.html" class="nav-link" data-page="tenders">Tenders</a></li>
|
|
<li><a href="/alerts.html" class="nav-link" data-page="alerts">Alerts</a></li>
|
|
<li><a href="/profile.html" class="nav-link" data-page="profile">Profile</a></li>
|
|
</ul>
|
|
|
|
<!-- User Menu -->
|
|
<div class="nav-user">
|
|
<button class="user-menu-toggle" aria-label="User menu">
|
|
<span class="user-avatar">${userEmail.charAt(0).toUpperCase()}</span>
|
|
<span class="user-email">${userEmail}</span>
|
|
<svg class="dropdown-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
</svg>
|
|
</button>
|
|
<div class="user-dropdown" style="display: none;">
|
|
<a href="/profile.html" class="dropdown-link">Profile Settings</a>
|
|
<button class="dropdown-link logout-btn">Logout</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* HTML for unauthenticated users
|
|
*/
|
|
getUnauthenticatedNavHTML() {
|
|
return `
|
|
<header class="app-header">
|
|
<div class="nav-container">
|
|
<!-- Logo / Brand -->
|
|
<div class="nav-brand">
|
|
<a href="/index.html" class="brand-link">
|
|
<img src="/logo.png" alt="TenderRadar" class="nav-logo">
|
|
<span class="brand-text">TenderRadar</span>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Mobile Toggle -->
|
|
<button class="mobile-menu-toggle" aria-label="Toggle menu">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</button>
|
|
|
|
<!-- Auth Links -->
|
|
<div class="nav-menu-wrapper">
|
|
<div class="nav-auth">
|
|
<a href="/login.html" class="btn btn-outline btn-sm">Login</a>
|
|
<a href="/signup.html" class="btn btn-primary btn-sm">Sign Up</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Attach event listeners to navbar
|
|
*/
|
|
attachEventListeners() {
|
|
if (!this.isLoggedIn) return;
|
|
|
|
// Mobile menu toggle
|
|
const mobileToggle = this.navElement.querySelector('.mobile-menu-toggle');
|
|
const navWrapper = this.navElement.querySelector('.nav-menu-wrapper');
|
|
|
|
mobileToggle.addEventListener('click', () => {
|
|
navWrapper.classList.toggle('active');
|
|
mobileToggle.classList.toggle('active');
|
|
});
|
|
|
|
// User menu dropdown
|
|
const userToggle = this.navElement.querySelector('.user-menu-toggle');
|
|
const userDropdown = this.navElement.querySelector('.user-dropdown');
|
|
|
|
userToggle.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
userDropdown.style.display =
|
|
userDropdown.style.display === 'none' ? 'block' : 'none';
|
|
});
|
|
|
|
// Close dropdown when clicking elsewhere
|
|
document.addEventListener('click', () => {
|
|
userDropdown.style.display = 'none';
|
|
});
|
|
|
|
// Logout button
|
|
const logoutBtn = this.navElement.querySelector('.logout-btn');
|
|
logoutBtn.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
logout();
|
|
});
|
|
|
|
// Close mobile menu when clicking a link
|
|
const navLinks = this.navElement.querySelectorAll('.nav-link');
|
|
navLinks.forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
navWrapper.classList.remove('active');
|
|
mobileToggle.classList.remove('active');
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Highlight the current page's nav link
|
|
*/
|
|
highlightActivePage() {
|
|
const currentPath = window.location.pathname;
|
|
const navLinks = this.navElement.querySelectorAll('.nav-link');
|
|
|
|
navLinks.forEach(link => {
|
|
const href = link.getAttribute('href');
|
|
if (currentPath.includes(href.replace('.html', ''))) {
|
|
link.classList.add('active');
|
|
} else {
|
|
link.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Auto-initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const navbar = new NavBar();
|
|
navbar.init();
|
|
});
|
|
} else {
|
|
const navbar = new NavBar();
|
|
navbar.init();
|
|
}
|