171 lines
8.0 KiB
HTML
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>© 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>
|
|
|
|
|