From 77d913fe50d4f3259f82c37d4cd0ed2edf7896ce Mon Sep 17 00:00:00 2001
From: Phillip Tarrant
Date: Wed, 26 Nov 2025 10:21:46 -0600
Subject: [PATCH] feat(web): add navigation menu bar for logged-in users
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add horizontal nav menu with 7 items: Profile, Characters, Sessions,
Mechanics, Leaderboard, Settings, Help
- Implement responsive hamburger menu for mobile (≤768px)
- Create pages blueprint with stub routes for new pages
- Add "Coming Soon" styled stub templates with icons
- Include active state highlighting for current page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude
---
public_web/app/__init__.py | 4 +-
public_web/app/views/pages.py | 96 ++++++++
public_web/static/css/main.css | 254 ++++++++++++++++++++
public_web/templates/base.html | 71 +++++-
public_web/templates/pages/help.html | 25 ++
public_web/templates/pages/leaderboard.html | 28 +++
public_web/templates/pages/mechanics.html | 24 ++
public_web/templates/pages/profile.html | 24 ++
public_web/templates/pages/sessions.html | 27 +++
public_web/templates/pages/settings.html | 24 ++
10 files changed, 573 insertions(+), 4 deletions(-)
create mode 100644 public_web/app/views/pages.py
create mode 100644 public_web/templates/pages/help.html
create mode 100644 public_web/templates/pages/leaderboard.html
create mode 100644 public_web/templates/pages/mechanics.html
create mode 100644 public_web/templates/pages/profile.html
create mode 100644 public_web/templates/pages/sessions.html
create mode 100644 public_web/templates/pages/settings.html
diff --git a/public_web/app/__init__.py b/public_web/app/__init__.py
index 1f5c8a6..9b22a14 100644
--- a/public_web/app/__init__.py
+++ b/public_web/app/__init__.py
@@ -57,10 +57,12 @@ def create_app():
from .views.auth_views import auth_bp
from .views.character_views import character_bp
from .views.game_views import game_bp
+ from .views.pages import pages_bp
app.register_blueprint(auth_bp)
app.register_blueprint(character_bp)
app.register_blueprint(game_bp)
+ app.register_blueprint(pages_bp)
# Register Jinja filters
def format_timestamp(iso_string: str) -> str:
@@ -107,6 +109,6 @@ def create_app():
logger.error("internal_server_error", error=str(error))
return render_template('errors/500.html'), 500
- logger.info("flask_app_created", blueprints=["auth", "character", "game"])
+ logger.info("flask_app_created", blueprints=["auth", "character", "game", "pages"])
return app
diff --git a/public_web/app/views/pages.py b/public_web/app/views/pages.py
new file mode 100644
index 0000000..ed7130c
--- /dev/null
+++ b/public_web/app/views/pages.py
@@ -0,0 +1,96 @@
+"""
+Pages Views Blueprint
+
+This module provides web UI routes for static/stub pages:
+- Profile page
+- Sessions page
+- Mechanics page
+- Leaderboard page
+- Settings page
+- Help page
+
+These are currently stub pages that will be implemented in future phases.
+"""
+
+from flask import Blueprint, render_template
+from app.utils.auth import require_auth_web
+from app.utils.logging import get_logger
+
+
+# Initialize logger
+logger = get_logger(__file__)
+
+# Create blueprint
+pages_bp = Blueprint('pages', __name__)
+
+
+@pages_bp.route('/profile')
+@require_auth_web
+def profile():
+ """
+ Display player profile page.
+
+ Currently a stub - will show player stats, achievements, etc.
+ """
+ logger.info("Accessing profile page")
+ return render_template('pages/profile.html')
+
+
+@pages_bp.route('/sessions')
+@require_auth_web
+def sessions():
+ """
+ Display active game sessions page.
+
+ Currently a stub - will show list of active/past sessions.
+ """
+ logger.info("Accessing sessions page")
+ return render_template('pages/sessions.html')
+
+
+@pages_bp.route('/mechanics')
+@require_auth_web
+def mechanics():
+ """
+ Display game mechanics reference page.
+
+ Currently a stub - will explain combat, skills, items, etc.
+ """
+ logger.info("Accessing mechanics page")
+ return render_template('pages/mechanics.html')
+
+
+@pages_bp.route('/leaderboard')
+@require_auth_web
+def leaderboard():
+ """
+ Display leaderboard page.
+
+ Currently a stub - will show top players, achievements, etc.
+ """
+ logger.info("Accessing leaderboard page")
+ return render_template('pages/leaderboard.html')
+
+
+@pages_bp.route('/settings')
+@require_auth_web
+def settings():
+ """
+ Display user settings page.
+
+ Currently a stub - will allow account settings, preferences, etc.
+ """
+ logger.info("Accessing settings page")
+ return render_template('pages/settings.html')
+
+
+@pages_bp.route('/help')
+@require_auth_web
+def help_page():
+ """
+ Display help/guide page.
+
+ Currently a stub - will provide tutorials, FAQs, support info.
+ """
+ logger.info("Accessing help page")
+ return render_template('pages/help.html')
diff --git a/public_web/static/css/main.css b/public_web/static/css/main.css
index dc5ed65..68f16d2 100644
--- a/public_web/static/css/main.css
+++ b/public_web/static/css/main.css
@@ -130,6 +130,207 @@ body {
text-shadow: 0 0 8px rgba(243, 156, 18, 0.3);
}
+/* ===== NAVIGATION MENU ===== */
+.nav-menu {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.nav-item {
+ font-family: var(--font-body);
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+ text-decoration: none;
+ padding: 0.5rem 0.75rem;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+}
+
+.nav-item:hover {
+ color: var(--accent-gold);
+ background: rgba(243, 156, 18, 0.1);
+}
+
+.nav-item.active {
+ color: var(--accent-gold);
+ background: rgba(243, 156, 18, 0.15);
+ font-weight: 600;
+}
+
+/* Header User Section */
+.header-user {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding-left: 1rem;
+ border-left: 1px solid var(--border-primary);
+}
+
+/* ===== HAMBURGER MENU (Mobile) ===== */
+.hamburger-btn {
+ display: none;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0.5rem;
+ z-index: 1001;
+}
+
+.hamburger-icon {
+ display: block;
+ width: 24px;
+ height: 2px;
+ background: var(--text-primary);
+ position: relative;
+ transition: all 0.3s ease;
+}
+
+.hamburger-icon::before,
+.hamburger-icon::after {
+ content: '';
+ position: absolute;
+ width: 24px;
+ height: 2px;
+ background: var(--text-primary);
+ left: 0;
+ transition: all 0.3s ease;
+}
+
+.hamburger-icon::before {
+ top: -8px;
+}
+
+.hamburger-icon::after {
+ bottom: -8px;
+}
+
+/* Hamburger animation when open */
+.hamburger-btn.open .hamburger-icon {
+ background: transparent;
+}
+
+.hamburger-btn.open .hamburger-icon::before {
+ transform: rotate(45deg);
+ top: 0;
+ background: var(--accent-gold);
+}
+
+.hamburger-btn.open .hamburger-icon::after {
+ transform: rotate(-45deg);
+ bottom: 0;
+ background: var(--accent-gold);
+}
+
+/* ===== MOBILE MENU ===== */
+.mobile-menu {
+ display: none;
+ flex-direction: column;
+ background: var(--bg-secondary);
+ border-top: 1px solid var(--border-primary);
+ padding: 1rem;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ box-shadow: var(--shadow-lg);
+}
+
+.mobile-menu.open {
+ display: flex;
+}
+
+.mobile-nav-item {
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ color: var(--text-secondary);
+ text-decoration: none;
+ padding: 0.75rem 1rem;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+}
+
+.mobile-nav-item:hover {
+ color: var(--accent-gold);
+ background: rgba(243, 156, 18, 0.1);
+}
+
+.mobile-nav-item.active {
+ color: var(--accent-gold);
+ background: rgba(243, 156, 18, 0.15);
+ font-weight: 600;
+}
+
+.mobile-menu-divider {
+ height: 1px;
+ background: var(--border-primary);
+ margin: 0.75rem 0;
+}
+
+.mobile-user-greeting {
+ font-size: var(--text-sm);
+ color: var(--text-muted);
+ padding: 0.5rem 1rem;
+}
+
+.mobile-logout {
+ color: var(--accent-red-light);
+ background: none;
+ border: none;
+ text-align: left;
+ cursor: pointer;
+ width: 100%;
+}
+
+.mobile-logout:hover {
+ color: var(--accent-red);
+ background: rgba(192, 57, 43, 0.1);
+}
+
+/* ===== RESPONSIVE HEADER ===== */
+@media (max-width: 992px) {
+ .nav-item {
+ padding: 0.4rem 0.5rem;
+ font-size: var(--text-xs);
+ }
+
+ .nav-menu {
+ gap: 0.125rem;
+ }
+
+ .header-user {
+ padding-left: 0.75rem;
+ }
+
+ .user-greeting {
+ display: none;
+ }
+}
+
+@media (max-width: 768px) {
+ .header {
+ position: relative;
+ padding: 1rem;
+ }
+
+ .nav-menu {
+ display: none;
+ }
+
+ .header-user {
+ display: none;
+ }
+
+ .hamburger-btn {
+ display: block;
+ }
+
+ .logo {
+ font-size: var(--text-xl);
+ }
+}
+
/* ===== MAIN CONTENT ===== */
main {
flex: 1;
@@ -606,3 +807,56 @@ main {
.hidden {
display: none;
}
+
+/* ===== PAGE CONTAINER ===== */
+.page-container {
+ width: 100%;
+ max-width: 600px;
+ padding: 2rem;
+}
+
+/* ===== COMING SOON CARD ===== */
+.coming-soon-card {
+ background: var(--bg-secondary);
+ border: 2px solid var(--border-ornate);
+ border-radius: 8px;
+ padding: 3rem 2rem;
+ text-align: center;
+ box-shadow: var(--shadow-lg);
+}
+
+.coming-soon-card .page-title {
+ margin-bottom: 1.5rem;
+}
+
+.coming-soon-icon {
+ color: var(--accent-gold);
+ margin-bottom: 1.5rem;
+ opacity: 0.8;
+}
+
+.coming-soon-icon svg {
+ width: 64px;
+ height: 64px;
+}
+
+.coming-soon-text {
+ font-family: var(--font-heading);
+ font-size: var(--text-2xl);
+ color: var(--text-secondary);
+ text-transform: uppercase;
+ letter-spacing: 3px;
+ margin-bottom: 1rem;
+}
+
+.coming-soon-description {
+ font-size: var(--text-base);
+ color: var(--text-muted);
+ margin-bottom: 2rem;
+ line-height: 1.6;
+}
+
+.coming-soon-card .btn {
+ width: auto;
+ display: inline-block;
+}
diff --git a/public_web/templates/base.html b/public_web/templates/base.html
index da0ce13..e7d6118 100644
--- a/public_web/templates/base.html
+++ b/public_web/templates/base.html
@@ -28,14 +28,49 @@
⚔️ Code of Conquest
{% if current_user %}
-
+
+
+
{% block scripts %}{% endblock %}