Files
urllists/static/templates/base.html
2025-11-30 12:19:31 +00:00

171 lines
8.0 KiB
HTML

<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ app_name }}{% endblock %}</title>
<link href="{{ url_for('css_output') }}" rel="stylesheet">
<script src="{{ url_for('js_auth') }}"></script>
{% block extra_head %}{% endblock %}
</head>
<body class="bg-slate-900 text-slate-100 min-h-screen flex flex-col">
<!-- Navigation -->
<nav class="bg-slate-800 border-b border-slate-700 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<div class="flex items-center flex-shrink-0">
<a href="{{ url_for('home') }}" class="text-2xl font-bold text-sky-400 hover:text-sky-300 transition-colors">
⚡ {{ app_name }}
</a>
</div>
<!-- Mobile menu button -->
<div class="block sm:hidden">
<button id="mobileMenuBtn" class="inline-flex items-center justify-center p-2 rounded-md text-slate-300 hover:text-white hover:bg-slate-700 focus:outline-none focus:bg-slate-700 focus:text-white transition-colors">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<!-- Desktop Navigation -->
<div class="hidden sm:flex items-center space-x-1">
<a href="{{ url_for('home') }}" class="nav-link">Home</a>
<a id="dashboardLink" href="{{ url_for('dashboard') }}" class="nav-link hidden">Dashboard</a>
<div id="authBorder" class="border-l border-slate-600 mx-2 hidden"></div>
<a id="loginLink" href="{{ url_for('login') }}" class="nav-link">Login</a>
<a id="signupLink" href="{{ url_for('register') }}" class="btn-primary text-sm">Sign Up</a>
<a id="logoutLink" href="#" onclick="handleLogout(event)" class="nav-link hidden">Logout</a>
</div>
</div>
<!-- Mobile Navigation -->
<div id="mobileMenu" class="hidden sm:hidden pb-3 space-y-1">
<a href="{{ url_for('home') }}" class="nav-link block">Home</a>
<a id="mobileDashboardLink" href="{{ url_for('dashboard') }}" class="nav-link block hidden">Dashboard</a>
<a id="mobileLoginLink" href="{{ url_for('login') }}" class="nav-link block">Login</a>
<a id="mobileSignupLink" href="{{ url_for('register') }}" class="nav-link block text-sky-400 hover:text-sky-300">Sign Up</a>
<a id="mobileLogoutLink" href="#" onclick="handleLogout(event)" class="nav-link block text-red-400 hover:text-red-300 hidden">Logout</a>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="flex-1 w-full">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{% if messages %}
{% for message in messages %}
<div class="mb-6 alert alert-info">{{ message }}</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
</main>
<!-- Footer -->
<footer class="bg-slate-800 border-t border-slate-700 mt-12">
<div class="border-t border-slate-700 pt-8 text-center text-slate-400 text-sm">
<p>&copy; 2025 {{ app_name }}. All rights reserved.</p>
</div>
</footer>
<script>
// Mobile menu toggle
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const mobileMenu = document.getElementById('mobileMenu');
if (mobileMenuBtn) {
mobileMenuBtn.addEventListener('click', () => {
mobileMenu.classList.toggle('hidden');
});
}
// Handle logout
async function handleLogout(event) {
event.preventDefault();
try {
const token = localStorage.getItem('access_token');
if (token) {
// Call the backend logout API
await fetch('/api/auth/logout', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
}).catch(() => {
// Logout endpoint might not support POST with Bearer token
// That's OK, we'll still clear the session client-side
});
}
// Clear localStorage
localStorage.removeItem('access_token');
// Dispatch storage event for cross-tab sync
window.dispatchEvent(new StorageEvent('storage', {
key: 'access_token',
newValue: null,
oldValue: token,
storageArea: localStorage
}));
// Redirect to home
window.location.href = '{{ url_for("home") }}';
} catch (error) {
console.error('Logout error:', error);
// Still clear the token and redirect
localStorage.removeItem('access_token');
window.location.href = '{{ url_for("home") }}';
}
}
// Update navbar based on authentication status
function updateNavbar() {
const authenticated = isAuthenticated();
// Show/hide elements based on auth status
if (authenticated) {
// User is logged in
document.getElementById('dashboardLink').classList.remove('hidden');
document.getElementById('authBorder').classList.remove('hidden');
document.getElementById('loginLink').classList.add('hidden');
document.getElementById('signupLink').classList.add('hidden');
document.getElementById('logoutLink').classList.remove('hidden');
// Mobile
document.getElementById('mobileDashboardLink').classList.remove('hidden');
document.getElementById('mobileLoginLink').classList.add('hidden');
document.getElementById('mobileSignupLink').classList.add('hidden');
document.getElementById('mobileLogoutLink').classList.remove('hidden');
} else {
// User is not logged in
document.getElementById('dashboardLink').classList.add('hidden');
document.getElementById('authBorder').classList.add('hidden');
document.getElementById('loginLink').classList.remove('hidden');
document.getElementById('signupLink').classList.remove('hidden');
document.getElementById('logoutLink').classList.add('hidden');
// Mobile
document.getElementById('mobileDashboardLink').classList.add('hidden');
document.getElementById('mobileLoginLink').classList.remove('hidden');
document.getElementById('mobileSignupLink').classList.remove('hidden');
document.getElementById('mobileLogoutLink').classList.add('hidden');
}
}
// Call on page load and when auth state changes
document.addEventListener('DOMContentLoaded', updateNavbar);
// Listen for storage changes (when token is added/removed in another tab/window)
window.addEventListener('storage', updateNavbar);
</script>
{% block extra_scripts %}{% endblock %}
</body>
</html>